单列聚簇索引还有个需要注意的点,就是这个列的辨识度要搞,如果这个列只有 MAN 和 WOMAN 的话,为这个列建立索引根本就没有什么用处,因为跟全表扫描一样的效率,优化器更偏向于全表扫描,所以切记不要给列值只有寥寥几个的列建立索引,纯属浪费。
5.2 多列非聚簇索引
如果是 name + mobile 联合索引的话,需要注意的点就有点多了:
每个数据页的排序就会变成,先按照 name 进行排序,然后再按照 mobile 进行排序。
5.4 什么时候不会使用索引
1.不满足最左匹配
我想大家都听过 最左匹配原则,就是这个意思,因为我的索引是先根据 name 排序再根据 mobile 进行排序的。那么查询的时候,如果加上 order by 的话,这个排序同样也是派上用场的。
但是如果说查询条件只是写了 WHERE mobile = '13800x1',那上面的索引就完全派不上用场。
怎么说,因为我这个索引是结合了 name 先做排序然后索引的,你只是查询 mobile 的话,就需要遍历整个索引,拿到 id 后还需要回表,这个过程跟直接扫描 聚簇索引 相比,耗费的资源更多,所以,如果一个 name + mobile 的索引,只应用于带有 name 开始的查询条件。因为 name 在前面。这就是 最左匹配原则。
2.排序一个列使用升序一个使用降序
比如 WHERE name like 'xxx%' ORDER BY name DESC,mobile ASC,这样的情况只能用到 name 列的索引(也就是所有 mobile 失去了效果),然后再把所有列重新整理,依据 mobile 重新排序。
如果两个都是同一个方向的排序就不会出现这种情况。
3.VARCHAR 匹配前缀查询
WHERE name like '%狗蛋' 这种情况为什么不能使用索引呢,因为你前面未知啊,根据 name 排序的索引查询跟全表扫描有什么区别,还少了 回表 的操作,所以只能全表扫描,取出满足 xxx狗蛋 的数据了。
那如果我们在查询 左外连接 的时候,暗示着我得出的结果中,被驱动表不含 NULL 的值,聪明的暖男解析器将会 GET 到这个信息,把 外连接 优化成 内连接。
比如上面的例子中:
1 2 3 4 5 6 7
SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id; AND scp.id IS NOT NULL;
那么上面两个语句中是不是就都暗示解析器中,拿到的 scope 中的 id 不能为空。
所以!直接变成这条语句执行:
1 2 3 4 5 6
SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id;;
单列聚簇索引还有个需要注意的点,就是这个列的辨识度要搞,如果这个列只有 MAN 和 WOMAN 的话,为这个列建立索引根本就没有什么用处,因为跟全表扫描一样的效率,优化器更偏向于全表扫描,所以切记不要给列值只有寥寥几个的列建立索引,纯属浪费。
5.2 多列非聚簇索引
如果是 name + mobile 联合索引的话,需要注意的点就有点多了:
每个数据页的排序就会变成,先按照 name 进行排序,然后再按照 mobile 进行排序。
5.4 什么时候不会使用索引
1.不满足最左匹配
我想大家都听过 最左匹配原则,就是这个意思,因为我的索引是先根据 name 排序再根据 mobile 进行排序的。那么查询的时候,如果加上 order by 的话,这个排序同样也是派上用场的。
但是如果说查询条件只是写了 WHERE mobile = '13800x1',那上面的索引就完全派不上用场。
怎么说,因为我这个索引是结合了 name 先做排序然后索引的,你只是查询 mobile 的话,就需要遍历整个索引,拿到 id 后还需要回表,这个过程跟直接扫描 聚簇索引 相比,耗费的资源更多,所以,如果一个 name + mobile 的索引,只应用于带有 name 开始的查询条件。因为 name 在前面。这就是 最左匹配原则。
2.排序一个列使用升序一个使用降序
比如 WHERE name like 'xxx%' ORDER BY name DESC,mobile ASC,这样的情况只能用到 name 列的索引(也就是所有 mobile 失去了效果),然后再把所有列重新整理,依据 mobile 重新排序。
如果两个都是同一个方向的排序就不会出现这种情况。
3.VARCHAR 匹配前缀查询
WHERE name like '%狗蛋' 这种情况为什么不能使用索引呢,因为你前面未知啊,根据 name 排序的索引查询跟全表扫描有什么区别,还少了 回表 的操作,所以只能全表扫描,取出满足 xxx狗蛋 的数据了。
那如果我们在查询 左外连接 的时候,暗示着我得出的结果中,被驱动表不含 NULL 的值,聪明的暖男解析器将会 GET 到这个信息,把 外连接 优化成 内连接。
比如上面的例子中:
1 2 3 4 5 6 7
SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id; AND scp.id IS NOT NULL;
那么上面两个语句中是不是就都暗示解析器中,拿到的 scope 中的 id 不能为空。
所以!直接变成这条语句执行:
1 2 3 4 5 6
SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id;;
单列聚簇索引还有个需要注意的点,就是这个列的辨识度要搞,如果这个列只有 MAN 和 WOMAN 的话,为这个列建立索引根本就没有什么用处,因为跟全表扫描一样的效率,优化器更偏向于全表扫描,所以切记不要给列值只有寥寥几个的列建立索引,纯属浪费。
5.2 多列非聚簇索引
如果是 name + mobile 联合索引的话,需要注意的点就有点多了:
每个数据页的排序就会变成,先按照 name 进行排序,然后再按照 mobile 进行排序。
5.4 什么时候不会使用索引
1.不满足最左匹配
我想大家都听过 最左匹配原则,就是这个意思,因为我的索引是先根据 name 排序再根据 mobile 进行排序的。那么查询的时候,如果加上 order by 的话,这个排序同样也是派上用场的。
但是如果说查询条件只是写了 WHERE mobile = '13800x1',那上面的索引就完全派不上用场。
怎么说,因为我这个索引是结合了 name 先做排序然后索引的,你只是查询 mobile 的话,就需要遍历整个索引,拿到 id 后还需要回表,这个过程跟直接扫描 聚簇索引 相比,耗费的资源更多,所以,如果一个 name + mobile 的索引,只应用于带有 name 开始的查询条件。因为 name 在前面。这就是 最左匹配原则。
2.排序一个列使用升序一个使用降序
比如 WHERE name like 'xxx%' ORDER BY name DESC,mobile ASC,这样的情况只能用到 name 列的索引(也就是所有 mobile 失去了效果),然后再把所有列重新整理,依据 mobile 重新排序。
如果两个都是同一个方向的排序就不会出现这种情况。
3.VARCHAR 匹配前缀查询
WHERE name like '%狗蛋' 这种情况为什么不能使用索引呢,因为你前面未知啊,根据 name 排序的索引查询跟全表扫描有什么区别,还少了 回表 的操作,所以只能全表扫描,取出满足 xxx狗蛋 的数据了。
那如果我们在查询 左外连接 的时候,暗示着我得出的结果中,被驱动表不含 NULL 的值,聪明的暖男解析器将会 GET 到这个信息,把 外连接 优化成 内连接。
比如上面的例子中:
1 2 3 4 5 6 7
SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id; AND scp.id IS NOT NULL;
那么上面两个语句中是不是就都暗示解析器中,拿到的 scope 中的 id 不能为空。
所以!直接变成这条语句执行:
1 2 3 4 5 6
SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id;;
单列聚簇索引还有个需要注意的点,就是这个列的辨识度要搞,如果这个列只有 MAN 和 WOMAN 的话,为这个列建立索引根本就没有什么用处,因为跟全表扫描一样的效率,优化器更偏向于全表扫描,所以切记不要给列值只有寥寥几个的列建立索引,纯属浪费。
5.2 多列非聚簇索引
如果是 name + mobile 联合索引的话,需要注意的点就有点多了:
每个数据页的排序就会变成,先按照 name 进行排序,然后再按照 mobile 进行排序。
5.4 什么时候不会使用索引
1.不满足最左匹配
我想大家都听过 最左匹配原则,就是这个意思,因为我的索引是先根据 name 排序再根据 mobile 进行排序的。那么查询的时候,如果加上 order by 的话,这个排序同样也是派上用场的。
但是如果说查询条件只是写了 WHERE mobile = '13800x1',那上面的索引就完全派不上用场。
怎么说,因为我这个索引是结合了 name 先做排序然后索引的,你只是查询 mobile 的话,就需要遍历整个索引,拿到 id 后还需要回表,这个过程跟直接扫描 聚簇索引 相比,耗费的资源更多,所以,如果一个 name + mobile 的索引,只应用于带有 name 开始的查询条件。因为 name 在前面。这就是 最左匹配原则。
2.排序一个列使用升序一个使用降序
比如 WHERE name like 'xxx%' ORDER BY name DESC,mobile ASC,这样的情况只能用到 name 列的索引(也就是所有 mobile 失去了效果),然后再把所有列重新整理,依据 mobile 重新排序。
如果两个都是同一个方向的排序就不会出现这种情况。
3.VARCHAR 匹配前缀查询
WHERE name like '%狗蛋' 这种情况为什么不能使用索引呢,因为你前面未知啊,根据 name 排序的索引查询跟全表扫描有什么区别,还少了 回表 的操作,所以只能全表扫描,取出满足 xxx狗蛋 的数据了。
那如果我们在查询 左外连接 的时候,暗示着我得出的结果中,被驱动表不含 NULL 的值,聪明的暖男解析器将会 GET 到这个信息,把 外连接 优化成 内连接。
比如上面的例子中:
1 2 3 4 5 6 7
SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id; AND scp.id IS NOT NULL;
那么上面两个语句中是不是就都暗示解析器中,拿到的 scope 中的 id 不能为空。
所以!直接变成这条语句执行:
1 2 3 4 5 6
SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id;;
单列聚簇索引还有个需要注意的点,就是这个列的辨识度要搞,如果这个列只有 MAN 和 WOMAN 的话,为这个列建立索引根本就没有什么用处,因为跟全表扫描一样的效率,优化器更偏向于全表扫描,所以切记不要给列值只有寥寥几个的列建立索引,纯属浪费。
5.2 多列非聚簇索引
如果是 name + mobile 联合索引的话,需要注意的点就有点多了:
每个数据页的排序就会变成,先按照 name 进行排序,然后再按照 mobile 进行排序。
5.4 什么时候不会使用索引
1.不满足最左匹配
我想大家都听过 最左匹配原则,就是这个意思,因为我的索引是先根据 name 排序再根据 mobile 进行排序的。那么查询的时候,如果加上 order by 的话,这个排序同样也是派上用场的。
但是如果说查询条件只是写了 WHERE mobile = '13800x1',那上面的索引就完全派不上用场。
怎么说,因为我这个索引是结合了 name 先做排序然后索引的,你只是查询 mobile 的话,就需要遍历整个索引,拿到 id 后还需要回表,这个过程跟直接扫描 聚簇索引 相比,耗费的资源更多,所以,如果一个 name + mobile 的索引,只应用于带有 name 开始的查询条件。因为 name 在前面。这就是 最左匹配原则。
2.排序一个列使用升序一个使用降序
比如 WHERE name like 'xxx%' ORDER BY name DESC,mobile ASC,这样的情况只能用到 name 列的索引(也就是所有 mobile 失去了效果),然后再把所有列重新整理,依据 mobile 重新排序。
如果两个都是同一个方向的排序就不会出现这种情况。
3.VARCHAR 匹配前缀查询
WHERE name like '%狗蛋' 这种情况为什么不能使用索引呢,因为你前面未知啊,根据 name 排序的索引查询跟全表扫描有什么区别,还少了 回表 的操作,所以只能全表扫描,取出满足 xxx狗蛋 的数据了。
那如果我们在查询 左外连接 的时候,暗示着我得出的结果中,被驱动表不含 NULL 的值,聪明的暖男解析器将会 GET 到这个信息,把 外连接 优化成 内连接。
比如上面的例子中:
1 2 3 4 5 6 7
SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id; AND scp.id IS NOT NULL;
那么上面两个语句中是不是就都暗示解析器中,拿到的 scope 中的 id 不能为空。
所以!直接变成这条语句执行:
1 2 3 4 5 6
SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id;;
单列聚簇索引还有个需要注意的点,就是这个列的辨识度要搞,如果这个列只有 MAN 和 WOMAN 的话,为这个列建立索引根本就没有什么用处,因为跟全表扫描一样的效率,优化器更偏向于全表扫描,所以切记不要给列值只有寥寥几个的列建立索引,纯属浪费。
5.2 多列非聚簇索引
如果是 name + mobile 联合索引的话,需要注意的点就有点多了:
每个数据页的排序就会变成,先按照 name 进行排序,然后再按照 mobile 进行排序。
5.4 什么时候不会使用索引
1.不满足最左匹配
我想大家都听过 最左匹配原则,就是这个意思,因为我的索引是先根据 name 排序再根据 mobile 进行排序的。那么查询的时候,如果加上 order by 的话,这个排序同样也是派上用场的。
但是如果说查询条件只是写了 WHERE mobile = '13800x1',那上面的索引就完全派不上用场。
怎么说,因为我这个索引是结合了 name 先做排序然后索引的,你只是查询 mobile 的话,就需要遍历整个索引,拿到 id 后还需要回表,这个过程跟直接扫描 聚簇索引 相比,耗费的资源更多,所以,如果一个 name + mobile 的索引,只应用于带有 name 开始的查询条件。因为 name 在前面。这就是 最左匹配原则。
2.排序一个列使用升序一个使用降序
比如 WHERE name like 'xxx%' ORDER BY name DESC,mobile ASC,这样的情况只能用到 name 列的索引(也就是所有 mobile 失去了效果),然后再把所有列重新整理,依据 mobile 重新排序。
如果两个都是同一个方向的排序就不会出现这种情况。
3.VARCHAR 匹配前缀查询
WHERE name like '%狗蛋' 这种情况为什么不能使用索引呢,因为你前面未知啊,根据 name 排序的索引查询跟全表扫描有什么区别,还少了 回表 的操作,所以只能全表扫描,取出满足 xxx狗蛋 的数据了。
那如果我们在查询 左外连接 的时候,暗示着我得出的结果中,被驱动表不含 NULL 的值,聪明的暖男解析器将会 GET 到这个信息,把 外连接 优化成 内连接。
比如上面的例子中:
1 2 3 4 5 6 7
SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id; AND scp.id IS NOT NULL;
那么上面两个语句中是不是就都暗示解析器中,拿到的 scope 中的 id 不能为空。
所以!直接变成这条语句执行:
1 2 3 4 5 6
SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id;;
单列聚簇索引还有个需要注意的点,就是这个列的辨识度要搞,如果这个列只有 MAN 和 WOMAN 的话,为这个列建立索引根本就没有什么用处,因为跟全表扫描一样的效率,优化器更偏向于全表扫描,所以切记不要给列值只有寥寥几个的列建立索引,纯属浪费。
5.2 多列非聚簇索引
如果是 name + mobile 联合索引的话,需要注意的点就有点多了:
每个数据页的排序就会变成,先按照 name 进行排序,然后再按照 mobile 进行排序。
5.4 什么时候不会使用索引
1.不满足最左匹配
我想大家都听过 最左匹配原则,就是这个意思,因为我的索引是先根据 name 排序再根据 mobile 进行排序的。那么查询的时候,如果加上 order by 的话,这个排序同样也是派上用场的。
但是如果说查询条件只是写了 WHERE mobile = '13800x1',那上面的索引就完全派不上用场。
怎么说,因为我这个索引是结合了 name 先做排序然后索引的,你只是查询 mobile 的话,就需要遍历整个索引,拿到 id 后还需要回表,这个过程跟直接扫描 聚簇索引 相比,耗费的资源更多,所以,如果一个 name + mobile 的索引,只应用于带有 name 开始的查询条件。因为 name 在前面。这就是 最左匹配原则。
2.排序一个列使用升序一个使用降序
比如 WHERE name like 'xxx%' ORDER BY name DESC,mobile ASC,这样的情况只能用到 name 列的索引(也就是所有 mobile 失去了效果),然后再把所有列重新整理,依据 mobile 重新排序。
如果两个都是同一个方向的排序就不会出现这种情况。
3.VARCHAR 匹配前缀查询
WHERE name like '%狗蛋' 这种情况为什么不能使用索引呢,因为你前面未知啊,根据 name 排序的索引查询跟全表扫描有什么区别,还少了 回表 的操作,所以只能全表扫描,取出满足 xxx狗蛋 的数据了。
那如果我们在查询 左外连接 的时候,暗示着我得出的结果中,被驱动表不含 NULL 的值,聪明的暖男解析器将会 GET 到这个信息,把 外连接 优化成 内连接。
比如上面的例子中:
1 2 3 4 5 6 7
SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id; AND scp.id IS NOT NULL;
那么上面两个语句中是不是就都暗示解析器中,拿到的 scope 中的 id 不能为空。
所以!直接变成这条语句执行:
1 2 3 4 5 6
SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id;;
单列聚簇索引还有个需要注意的点,就是这个列的辨识度要搞,如果这个列只有 MAN 和 WOMAN 的话,为这个列建立索引根本就没有什么用处,因为跟全表扫描一样的效率,优化器更偏向于全表扫描,所以切记不要给列值只有寥寥几个的列建立索引,纯属浪费。
5.2 多列非聚簇索引
如果是 name + mobile 联合索引的话,需要注意的点就有点多了:
每个数据页的排序就会变成,先按照 name 进行排序,然后再按照 mobile 进行排序。
5.4 什么时候不会使用索引
1.不满足最左匹配
我想大家都听过 最左匹配原则,就是这个意思,因为我的索引是先根据 name 排序再根据 mobile 进行排序的。那么查询的时候,如果加上 order by 的话,这个排序同样也是派上用场的。
但是如果说查询条件只是写了 WHERE mobile = '13800x1',那上面的索引就完全派不上用场。
怎么说,因为我这个索引是结合了 name 先做排序然后索引的,你只是查询 mobile 的话,就需要遍历整个索引,拿到 id 后还需要回表,这个过程跟直接扫描 聚簇索引 相比,耗费的资源更多,所以,如果一个 name + mobile 的索引,只应用于带有 name 开始的查询条件。因为 name 在前面。这就是 最左匹配原则。
2.排序一个列使用升序一个使用降序
比如 WHERE name like 'xxx%' ORDER BY name DESC,mobile ASC,这样的情况只能用到 name 列的索引(也就是所有 mobile 失去了效果),然后再把所有列重新整理,依据 mobile 重新排序。
如果两个都是同一个方向的排序就不会出现这种情况。
3.VARCHAR 匹配前缀查询
WHERE name like '%狗蛋' 这种情况为什么不能使用索引呢,因为你前面未知啊,根据 name 排序的索引查询跟全表扫描有什么区别,还少了 回表 的操作,所以只能全表扫描,取出满足 xxx狗蛋 的数据了。
那如果我们在查询 左外连接 的时候,暗示着我得出的结果中,被驱动表不含 NULL 的值,聪明的暖男解析器将会 GET 到这个信息,把 外连接 优化成 内连接。
比如上面的例子中:
1 2 3 4 5 6 7
SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id; AND scp.id IS NOT NULL;
那么上面两个语句中是不是就都暗示解析器中,拿到的 scope 中的 id 不能为空。
所以!直接变成这条语句执行:
1 2 3 4 5 6
SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id;;
单列聚簇索引还有个需要注意的点,就是这个列的辨识度要搞,如果这个列只有 MAN 和 WOMAN 的话,为这个列建立索引根本就没有什么用处,因为跟全表扫描一样的效率,优化器更偏向于全表扫描,所以切记不要给列值只有寥寥几个的列建立索引,纯属浪费。
5.2 多列非聚簇索引
如果是 name + mobile 联合索引的话,需要注意的点就有点多了:
每个数据页的排序就会变成,先按照 name 进行排序,然后再按照 mobile 进行排序。
5.4 什么时候不会使用索引
1.不满足最左匹配
我想大家都听过 最左匹配原则,就是这个意思,因为我的索引是先根据 name 排序再根据 mobile 进行排序的。那么查询的时候,如果加上 order by 的话,这个排序同样也是派上用场的。
但是如果说查询条件只是写了 WHERE mobile = '13800x1',那上面的索引就完全派不上用场。
怎么说,因为我这个索引是结合了 name 先做排序然后索引的,你只是查询 mobile 的话,就需要遍历整个索引,拿到 id 后还需要回表,这个过程跟直接扫描 聚簇索引 相比,耗费的资源更多,所以,如果一个 name + mobile 的索引,只应用于带有 name 开始的查询条件。因为 name 在前面。这就是 最左匹配原则。
2.排序一个列使用升序一个使用降序
比如 WHERE name like 'xxx%' ORDER BY name DESC,mobile ASC,这样的情况只能用到 name 列的索引(也就是所有 mobile 失去了效果),然后再把所有列重新整理,依据 mobile 重新排序。
如果两个都是同一个方向的排序就不会出现这种情况。
3.VARCHAR 匹配前缀查询
WHERE name like '%狗蛋' 这种情况为什么不能使用索引呢,因为你前面未知啊,根据 name 排序的索引查询跟全表扫描有什么区别,还少了 回表 的操作,所以只能全表扫描,取出满足 xxx狗蛋 的数据了。
那如果我们在查询 左外连接 的时候,暗示着我得出的结果中,被驱动表不含 NULL 的值,聪明的暖男解析器将会 GET 到这个信息,把 外连接 优化成 内连接。
比如上面的例子中:
1 2 3 4 5 6 7
SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id; AND scp.id IS NOT NULL;
那么上面两个语句中是不是就都暗示解析器中,拿到的 scope 中的 id 不能为空。
所以!直接变成这条语句执行:
1 2 3 4 5 6
SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id;;
单列聚簇索引还有个需要注意的点,就是这个列的辨识度要搞,如果这个列只有 MAN 和 WOMAN 的话,为这个列建立索引根本就没有什么用处,因为跟全表扫描一样的效率,优化器更偏向于全表扫描,所以切记不要给列值只有寥寥几个的列建立索引,纯属浪费。
5.2 多列非聚簇索引
如果是 name + mobile 联合索引的话,需要注意的点就有点多了:
每个数据页的排序就会变成,先按照 name 进行排序,然后再按照 mobile 进行排序。
5.4 什么时候不会使用索引
1.不满足最左匹配
我想大家都听过 最左匹配原则,就是这个意思,因为我的索引是先根据 name 排序再根据 mobile 进行排序的。那么查询的时候,如果加上 order by 的话,这个排序同样也是派上用场的。
但是如果说查询条件只是写了 WHERE mobile = '13800x1',那上面的索引就完全派不上用场。
怎么说,因为我这个索引是结合了 name 先做排序然后索引的,你只是查询 mobile 的话,就需要遍历整个索引,拿到 id 后还需要回表,这个过程跟直接扫描 聚簇索引 相比,耗费的资源更多,所以,如果一个 name + mobile 的索引,只应用于带有 name 开始的查询条件。因为 name 在前面。这就是 最左匹配原则。
2.排序一个列使用升序一个使用降序
比如 WHERE name like 'xxx%' ORDER BY name DESC,mobile ASC,这样的情况只能用到 name 列的索引(也就是所有 mobile 失去了效果),然后再把所有列重新整理,依据 mobile 重新排序。
如果两个都是同一个方向的排序就不会出现这种情况。
3.VARCHAR 匹配前缀查询
WHERE name like '%狗蛋' 这种情况为什么不能使用索引呢,因为你前面未知啊,根据 name 排序的索引查询跟全表扫描有什么区别,还少了 回表 的操作,所以只能全表扫描,取出满足 xxx狗蛋 的数据了。
那如果我们在查询 左外连接 的时候,暗示着我得出的结果中,被驱动表不含 NULL 的值,聪明的暖男解析器将会 GET 到这个信息,把 外连接 优化成 内连接。
比如上面的例子中:
1 2 3 4 5 6 7
SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id; AND scp.id IS NOT NULL;
那么上面两个语句中是不是就都暗示解析器中,拿到的 scope 中的 id 不能为空。
所以!直接变成这条语句执行:
1 2 3 4 5 6
SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id;;
单列聚簇索引还有个需要注意的点,就是这个列的辨识度要搞,如果这个列只有 MAN 和 WOMAN 的话,为这个列建立索引根本就没有什么用处,因为跟全表扫描一样的效率,优化器更偏向于全表扫描,所以切记不要给列值只有寥寥几个的列建立索引,纯属浪费。
5.2 多列非聚簇索引
如果是 name + mobile 联合索引的话,需要注意的点就有点多了:
每个数据页的排序就会变成,先按照 name 进行排序,然后再按照 mobile 进行排序。
5.4 什么时候不会使用索引
1.不满足最左匹配
我想大家都听过 最左匹配原则,就是这个意思,因为我的索引是先根据 name 排序再根据 mobile 进行排序的。那么查询的时候,如果加上 order by 的话,这个排序同样也是派上用场的。
但是如果说查询条件只是写了 WHERE mobile = '13800x1',那上面的索引就完全派不上用场。
怎么说,因为我这个索引是结合了 name 先做排序然后索引的,你只是查询 mobile 的话,就需要遍历整个索引,拿到 id 后还需要回表,这个过程跟直接扫描 聚簇索引 相比,耗费的资源更多,所以,如果一个 name + mobile 的索引,只应用于带有 name 开始的查询条件。因为 name 在前面。这就是 最左匹配原则。
2.排序一个列使用升序一个使用降序
比如 WHERE name like 'xxx%' ORDER BY name DESC,mobile ASC,这样的情况只能用到 name 列的索引(也就是所有 mobile 失去了效果),然后再把所有列重新整理,依据 mobile 重新排序。
如果两个都是同一个方向的排序就不会出现这种情况。
3.VARCHAR 匹配前缀查询
WHERE name like '%狗蛋' 这种情况为什么不能使用索引呢,因为你前面未知啊,根据 name 排序的索引查询跟全表扫描有什么区别,还少了 回表 的操作,所以只能全表扫描,取出满足 xxx狗蛋 的数据了。
那如果我们在查询 左外连接 的时候,暗示着我得出的结果中,被驱动表不含 NULL 的值,聪明的暖男解析器将会 GET 到这个信息,把 外连接 优化成 内连接。
比如上面的例子中:
1 2 3 4 5 6 7
SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id; AND scp.id IS NOT NULL;
那么上面两个语句中是不是就都暗示解析器中,拿到的 scope 中的 id 不能为空。
所以!直接变成这条语句执行:
1 2 3 4 5 6
SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id;;
单列聚簇索引还有个需要注意的点,就是这个列的辨识度要搞,如果这个列只有 MAN 和 WOMAN 的话,为这个列建立索引根本就没有什么用处,因为跟全表扫描一样的效率,优化器更偏向于全表扫描,所以切记不要给列值只有寥寥几个的列建立索引,纯属浪费。
5.2 多列非聚簇索引
如果是 name + mobile 联合索引的话,需要注意的点就有点多了:
每个数据页的排序就会变成,先按照 name 进行排序,然后再按照 mobile 进行排序。
5.4 什么时候不会使用索引
1.不满足最左匹配
我想大家都听过 最左匹配原则,就是这个意思,因为我的索引是先根据 name 排序再根据 mobile 进行排序的。那么查询的时候,如果加上 order by 的话,这个排序同样也是派上用场的。
但是如果说查询条件只是写了 WHERE mobile = '13800x1',那上面的索引就完全派不上用场。
怎么说,因为我这个索引是结合了 name 先做排序然后索引的,你只是查询 mobile 的话,就需要遍历整个索引,拿到 id 后还需要回表,这个过程跟直接扫描 聚簇索引 相比,耗费的资源更多,所以,如果一个 name + mobile 的索引,只应用于带有 name 开始的查询条件。因为 name 在前面。这就是 最左匹配原则。
2.排序一个列使用升序一个使用降序
比如 WHERE name like 'xxx%' ORDER BY name DESC,mobile ASC,这样的情况只能用到 name 列的索引(也就是所有 mobile 失去了效果),然后再把所有列重新整理,依据 mobile 重新排序。
如果两个都是同一个方向的排序就不会出现这种情况。
3.VARCHAR 匹配前缀查询
WHERE name like '%狗蛋' 这种情况为什么不能使用索引呢,因为你前面未知啊,根据 name 排序的索引查询跟全表扫描有什么区别,还少了 回表 的操作,所以只能全表扫描,取出满足 xxx狗蛋 的数据了。
那如果我们在查询 左外连接 的时候,暗示着我得出的结果中,被驱动表不含 NULL 的值,聪明的暖男解析器将会 GET 到这个信息,把 外连接 优化成 内连接。
比如上面的例子中:
1 2 3 4 5 6 7
SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id; AND scp.id IS NOT NULL;
那么上面两个语句中是不是就都暗示解析器中,拿到的 scope 中的 id 不能为空。
所以!直接变成这条语句执行:
1 2 3 4 5 6
SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id;;
单列聚簇索引还有个需要注意的点,就是这个列的辨识度要搞,如果这个列只有 MAN 和 WOMAN 的话,为这个列建立索引根本就没有什么用处,因为跟全表扫描一样的效率,优化器更偏向于全表扫描,所以切记不要给列值只有寥寥几个的列建立索引,纯属浪费。
5.2 多列非聚簇索引
如果是 name + mobile 联合索引的话,需要注意的点就有点多了:
每个数据页的排序就会变成,先按照 name 进行排序,然后再按照 mobile 进行排序。
5.4 什么时候不会使用索引
1.不满足最左匹配
我想大家都听过 最左匹配原则,就是这个意思,因为我的索引是先根据 name 排序再根据 mobile 进行排序的。那么查询的时候,如果加上 order by 的话,这个排序同样也是派上用场的。
但是如果说查询条件只是写了 WHERE mobile = '13800x1',那上面的索引就完全派不上用场。
怎么说,因为我这个索引是结合了 name 先做排序然后索引的,你只是查询 mobile 的话,就需要遍历整个索引,拿到 id 后还需要回表,这个过程跟直接扫描 聚簇索引 相比,耗费的资源更多,所以,如果一个 name + mobile 的索引,只应用于带有 name 开始的查询条件。因为 name 在前面。这就是 最左匹配原则。
2.排序一个列使用升序一个使用降序
比如 WHERE name like 'xxx%' ORDER BY name DESC,mobile ASC,这样的情况只能用到 name 列的索引(也就是所有 mobile 失去了效果),然后再把所有列重新整理,依据 mobile 重新排序。
如果两个都是同一个方向的排序就不会出现这种情况。
3.VARCHAR 匹配前缀查询
WHERE name like '%狗蛋' 这种情况为什么不能使用索引呢,因为你前面未知啊,根据 name 排序的索引查询跟全表扫描有什么区别,还少了 回表 的操作,所以只能全表扫描,取出满足 xxx狗蛋 的数据了。
那如果我们在查询 左外连接 的时候,暗示着我得出的结果中,被驱动表不含 NULL 的值,聪明的暖男解析器将会 GET 到这个信息,把 外连接 优化成 内连接。
比如上面的例子中:
1 2 3 4 5 6 7
SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id; AND scp.id IS NOT NULL;
那么上面两个语句中是不是就都暗示解析器中,拿到的 scope 中的 id 不能为空。
所以!直接变成这条语句执行:
1 2 3 4 5 6
SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id;;
单列聚簇索引还有个需要注意的点,就是这个列的辨识度要搞,如果这个列只有 MAN 和 WOMAN 的话,为这个列建立索引根本就没有什么用处,因为跟全表扫描一样的效率,优化器更偏向于全表扫描,所以切记不要给列值只有寥寥几个的列建立索引,纯属浪费。
5.2 多列非聚簇索引
如果是 name + mobile 联合索引的话,需要注意的点就有点多了:
每个数据页的排序就会变成,先按照 name 进行排序,然后再按照 mobile 进行排序。
5.4 什么时候不会使用索引
1.不满足最左匹配
我想大家都听过 最左匹配原则,就是这个意思,因为我的索引是先根据 name 排序再根据 mobile 进行排序的。那么查询的时候,如果加上 order by 的话,这个排序同样也是派上用场的。
但是如果说查询条件只是写了 WHERE mobile = '13800x1',那上面的索引就完全派不上用场。
怎么说,因为我这个索引是结合了 name 先做排序然后索引的,你只是查询 mobile 的话,就需要遍历整个索引,拿到 id 后还需要回表,这个过程跟直接扫描 聚簇索引 相比,耗费的资源更多,所以,如果一个 name + mobile 的索引,只应用于带有 name 开始的查询条件。因为 name 在前面。这就是 最左匹配原则。
2.排序一个列使用升序一个使用降序
比如 WHERE name like 'xxx%' ORDER BY name DESC,mobile ASC,这样的情况只能用到 name 列的索引(也就是所有 mobile 失去了效果),然后再把所有列重新整理,依据 mobile 重新排序。
如果两个都是同一个方向的排序就不会出现这种情况。
3.VARCHAR 匹配前缀查询
WHERE name like '%狗蛋' 这种情况为什么不能使用索引呢,因为你前面未知啊,根据 name 排序的索引查询跟全表扫描有什么区别,还少了 回表 的操作,所以只能全表扫描,取出满足 xxx狗蛋 的数据了。
那如果我们在查询 左外连接 的时候,暗示着我得出的结果中,被驱动表不含 NULL 的值,聪明的暖男解析器将会 GET 到这个信息,把 外连接 优化成 内连接。
比如上面的例子中:
1 2 3 4 5 6 7
SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id; AND scp.id IS NOT NULL;
那么上面两个语句中是不是就都暗示解析器中,拿到的 scope 中的 id 不能为空。
所以!直接变成这条语句执行:
1 2 3 4 5 6
SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id;;
九.完
其实上面的例子我使用的是 id 来做例子,但是每条记录还带有其他所有列的信息(非 NULL)的,如果有 NULL 则会被记录在记录头信息,然后节省了存储数据的空间。
单列聚簇索引还有个需要注意的点,就是这个列的辨识度要搞,如果这个列只有 MAN 和 WOMAN 的话,为这个列建立索引根本就没有什么用处,因为跟全表扫描一样的效率,优化器更偏向于全表扫描,所以切记不要给列值只有寥寥几个的列建立索引,纯属浪费。
5.2 多列非聚簇索引
如果是 name + mobile 联合索引的话,需要注意的点就有点多了:
每个数据页的排序就会变成,先按照 name 进行排序,然后再按照 mobile 进行排序。
5.4 什么时候不会使用索引
1.不满足最左匹配
我想大家都听过 最左匹配原则,就是这个意思,因为我的索引是先根据 name 排序再根据 mobile 进行排序的。那么查询的时候,如果加上 order by 的话,这个排序同样也是派上用场的。
但是如果说查询条件只是写了 WHERE mobile = '13800x1',那上面的索引就完全派不上用场。
怎么说,因为我这个索引是结合了 name 先做排序然后索引的,你只是查询 mobile 的话,就需要遍历整个索引,拿到 id 后还需要回表,这个过程跟直接扫描 聚簇索引 相比,耗费的资源更多,所以,如果一个 name + mobile 的索引,只应用于带有 name 开始的查询条件。因为 name 在前面。这就是 最左匹配原则。
2.排序一个列使用升序一个使用降序
比如 WHERE name like 'xxx%' ORDER BY name DESC,mobile ASC,这样的情况只能用到 name 列的索引(也就是所有 mobile 失去了效果),然后再把所有列重新整理,依据 mobile 重新排序。
如果两个都是同一个方向的排序就不会出现这种情况。
3.VARCHAR 匹配前缀查询
WHERE name like '%狗蛋' 这种情况为什么不能使用索引呢,因为你前面未知啊,根据 name 排序的索引查询跟全表扫描有什么区别,还少了 回表 的操作,所以只能全表扫描,取出满足 xxx狗蛋 的数据了。
那如果我们在查询 左外连接 的时候,暗示着我得出的结果中,被驱动表不含 NULL 的值,聪明的暖男解析器将会 GET 到这个信息,把 外连接 优化成 内连接。
比如上面的例子中:
1 2 3 4 5 6 7
SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id; AND scp.id IS NOT NULL;
那么上面两个语句中是不是就都暗示解析器中,拿到的 scope 中的 id 不能为空。
所以!直接变成这条语句执行:
1 2 3 4 5 6
SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id;;
九.完
那我们可以看到,数据行并没有覆盖所有数据,只有 name + id。那比如我们只是查询 name 或者 id 列,就可以直接在这里返回去了。
但是如果我还想要知道 mobile 列的话,这时候就需要在上面那棵树拿到 id,然后再去 id 那棵树查询其他的列信息。
单列聚簇索引还有个需要注意的点,就是这个列的辨识度要搞,如果这个列只有 MAN 和 WOMAN 的话,为这个列建立索引根本就没有什么用处,因为跟全表扫描一样的效率,优化器更偏向于全表扫描,所以切记不要给列值只有寥寥几个的列建立索引,纯属浪费。
5.2 多列非聚簇索引
如果是 name + mobile 联合索引的话,需要注意的点就有点多了:
每个数据页的排序就会变成,先按照 name 进行排序,然后再按照 mobile 进行排序。
5.4 什么时候不会使用索引
1.不满足最左匹配
我想大家都听过 最左匹配原则,就是这个意思,因为我的索引是先根据 name 排序再根据 mobile 进行排序的。那么查询的时候,如果加上 order by 的话,这个排序同样也是派上用场的。
但是如果说查询条件只是写了 WHERE mobile = '13800x1',那上面的索引就完全派不上用场。
怎么说,因为我这个索引是结合了 name 先做排序然后索引的,你只是查询 mobile 的话,就需要遍历整个索引,拿到 id 后还需要回表,这个过程跟直接扫描 聚簇索引 相比,耗费的资源更多,所以,如果一个 name + mobile 的索引,只应用于带有 name 开始的查询条件。因为 name 在前面。这就是 最左匹配原则。
2.排序一个列使用升序一个使用降序
比如 WHERE name like 'xxx%' ORDER BY name DESC,mobile ASC,这样的情况只能用到 name 列的索引(也就是所有 mobile 失去了效果),然后再把所有列重新整理,依据 mobile 重新排序。
如果两个都是同一个方向的排序就不会出现这种情况。
3.VARCHAR 匹配前缀查询
WHERE name like '%狗蛋' 这种情况为什么不能使用索引呢,因为你前面未知啊,根据 name 排序的索引查询跟全表扫描有什么区别,还少了 回表 的操作,所以只能全表扫描,取出满足 xxx狗蛋 的数据了。
那如果我们在查询 左外连接 的时候,暗示着我得出的结果中,被驱动表不含 NULL 的值,聪明的暖男解析器将会 GET 到这个信息,把 外连接 优化成 内连接。
比如上面的例子中:
1 2 3 4 5 6 7
SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id; AND scp.id IS NOT NULL;
那么上面两个语句中是不是就都暗示解析器中,拿到的 scope 中的 id 不能为空。
所以!直接变成这条语句执行:
1 2 3 4 5 6
SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id;;
单列聚簇索引还有个需要注意的点,就是这个列的辨识度要搞,如果这个列只有 MAN 和 WOMAN 的话,为这个列建立索引根本就没有什么用处,因为跟全表扫描一样的效率,优化器更偏向于全表扫描,所以切记不要给列值只有寥寥几个的列建立索引,纯属浪费。
5.2 多列非聚簇索引
如果是 name + mobile 联合索引的话,需要注意的点就有点多了:
每个数据页的排序就会变成,先按照 name 进行排序,然后再按照 mobile 进行排序。
5.4 什么时候不会使用索引
1.不满足最左匹配
我想大家都听过 最左匹配原则,就是这个意思,因为我的索引是先根据 name 排序再根据 mobile 进行排序的。那么查询的时候,如果加上 order by 的话,这个排序同样也是派上用场的。
但是如果说查询条件只是写了 WHERE mobile = '13800x1',那上面的索引就完全派不上用场。
怎么说,因为我这个索引是结合了 name 先做排序然后索引的,你只是查询 mobile 的话,就需要遍历整个索引,拿到 id 后还需要回表,这个过程跟直接扫描 聚簇索引 相比,耗费的资源更多,所以,如果一个 name + mobile 的索引,只应用于带有 name 开始的查询条件。因为 name 在前面。这就是 最左匹配原则。
2.排序一个列使用升序一个使用降序
比如 WHERE name like 'xxx%' ORDER BY name DESC,mobile ASC,这样的情况只能用到 name 列的索引(也就是所有 mobile 失去了效果),然后再把所有列重新整理,依据 mobile 重新排序。
如果两个都是同一个方向的排序就不会出现这种情况。
3.VARCHAR 匹配前缀查询
WHERE name like '%狗蛋' 这种情况为什么不能使用索引呢,因为你前面未知啊,根据 name 排序的索引查询跟全表扫描有什么区别,还少了 回表 的操作,所以只能全表扫描,取出满足 xxx狗蛋 的数据了。
那如果我们在查询 左外连接 的时候,暗示着我得出的结果中,被驱动表不含 NULL 的值,聪明的暖男解析器将会 GET 到这个信息,把 外连接 优化成 内连接。
比如上面的例子中:
1 2 3 4 5 6 7
SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id; AND scp.id IS NOT NULL;
那么上面两个语句中是不是就都暗示解析器中,拿到的 scope 中的 id 不能为空。
所以!直接变成这条语句执行:
1 2 3 4 5 6
SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id;;
九.完
每个数据页的排序就会变成,先按照 name 进行排序,然后再按照 mobile 进行排序。
5.4 什么时候不会使用索引
1.不满足最左匹配
我想大家都听过 最左匹配原则,就是这个意思,因为我的索引是先根据 name 排序再根据 mobile 进行排序的。那么查询的时候,如果加上 order by 的话,这个排序同样也是派上用场的。
但是如果说查询条件只是写了 WHERE mobile = '13800x1',那上面的索引就完全派不上用场。
怎么说,因为我这个索引是结合了 name 先做排序然后索引的,你只是查询 mobile 的话,就需要遍历整个索引,拿到 id 后还需要回表,这个过程跟直接扫描 聚簇索引 相比,耗费的资源更多,所以,如果一个 name + mobile 的索引,只应用于带有 name 开始的查询条件。因为 name 在前面。这就是 最左匹配原则。
2.排序一个列使用升序一个使用降序
比如 WHERE name like 'xxx%' ORDER BY name DESC,mobile ASC,这样的情况只能用到 name 列的索引(也就是所有 mobile 失去了效果),然后再把所有列重新整理,依据 mobile 重新排序。
如果两个都是同一个方向的排序就不会出现这种情况。
3.VARCHAR 匹配前缀查询
WHERE name like '%狗蛋' 这种情况为什么不能使用索引呢,因为你前面未知啊,根据 name 排序的索引查询跟全表扫描有什么区别,还少了 回表 的操作,所以只能全表扫描,取出满足 xxx狗蛋 的数据了。
那如果我们在查询 左外连接 的时候,暗示着我得出的结果中,被驱动表不含 NULL 的值,聪明的暖男解析器将会 GET 到这个信息,把 外连接 优化成 内连接。
比如上面的例子中:
1 2 3 4 5 6 7
SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu LEFTJOINscope scp ON scp.stu_id = stu.id; AND scp.id IS NOT NULL;
那么上面两个语句中是不是就都暗示解析器中,拿到的 scope 中的 id 不能为空。
所以!直接变成这条语句执行:
1 2 3 4 5 6
SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id AND scp.id = 1; SELECT * FROM student stu INNERJOINscope scp ON scp.stu_id = stu.id;;
privateclassSqlSessionInterceptorimplementsInvocationHandler{ @Override public Object invoke(Object proxy, Method method, Object[] args)throws Throwable { SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator); try { // 来到这里调用SqlSession的方法 Object result = method.invoke(sqlSession, args); if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) { // force commit even on non-dirty sessions because some databases require // a commit/rollback before calling close() sqlSession.commit(true); } return result; } catch (Throwable t) { Throwable unwrapped = unwrapThrowable(t); if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) { // release the connection to avoid a deadlock if the translator is no loaded. See issue #22 closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); sqlSession = null; Throwable translated = SqlSessionTemplate.this.exceptionTranslator .translateExceptionIfPossible((PersistenceException) unwrapped); if (translated != null) { unwrapped = translated; } } throw unwrapped; } finally { if (sqlSession != null) { closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); } } } }
OK,来到 DefaultSqlSession#selectOne:
1 2 3 4 5 6 7 8 9 10 11 12
@Override public <T> T selectOne(String statement, Object parameter){ // Popular vote was to return null on 0 results and throw exception on too many. List<T> list = this.selectList(statement, parameter); if (list.size() == 1) { return list.get(0); } elseif (list.size() > 1) { thrownew TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size()); } else { returnnull; } }
@Override publicvoidafterPropertiesSet(){ logger.debug( "Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer."); }
}
这个类实现了两个接口:
BeanFactoryAware:这个接口,被 Spring 扫描到的话,是会被 Spring 调用的,传递了当前 BeanFactory 实例,有助于我们后期可以对 BeanFactory 的操作;
由于 ClassPathMapperScanner 继承了 Spring 提供的 ClassPathBeanDefinitionScanner,所以可以直接把扫描注解这件事情交给 Spring 来做,
1 2 3 4 5 6 7 8 9 10 11 12 13
@Override public Set<BeanDefinitionHolder> doScan(String... basePackages){ Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) { LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration."); } else { processBeanDefinitions(beanDefinitions); }
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) { if (explicitFactoryUsed) { LOGGER.warn( () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."); } definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName)); explicitFactoryUsed = true; } elseif (this.sqlSessionTemplate != null) { if (explicitFactoryUsed) { LOGGER.warn( () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."); } definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate); explicitFactoryUsed = true; }
// 根据类型注入 if (!explicitFactoryUsed) { LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'."); definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); } definition.setLazyInit(lazyInitialization); } }
/* public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); } */
}
解析Configuration.xml
1 2 3 4 5 6 7 8 9
// XMLConfigBuilder.java public Configuration parse(){ if (parsed) { thrownew BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; parseConfiguration(parser.evalNode("/configuration")); return configuration; }
privatevoidconfigurationElement(XNode context){ try { String namespace = context.getStringAttribute("namespace"); if (namespace == null namespace.equals("")) { thrownew BuilderException("Mapper's namespace cannot be empty"); } builderAssistant.setCurrentNamespace(namespace); cacheRefElement(context.evalNode("cache-ref")); cacheElement(context.evalNode("cache")); parameterMapElement(context.evalNodes("/mapper/parameterMap")); resultMapElements(context.evalNodes("/mapper/resultMap")); sqlElement(context.evalNodes("/mapper/sql")); buildStatementFromContext(context.evalNodes("selectinsertupdatedelete")); } catch (Exception e) { thrownew BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e); } }
结合 Mapper.xml 来看看:
1 2 3 4 5 6 7 8
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="sample.mybatis.mapper.HotelMapper"> <select id="selectByCityId" resultType="Hotel"> select city, name, address, zip from hotel where city = #{id} </select> </mapper>
// Include Fragments before parsing XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant); includeParser.applyIncludes(context.getNode());
privatevoidbindMapperForNamespace(){ String namespace = builderAssistant.getCurrentNamespace(); if (namespace != null) { Class<?> boundType = null; try { // 解析接口类型 boundType = Resources.classForName(namespace); } catch (ClassNotFoundException e) { //ignore, bound type is not required } if (boundType != null) { if (!configuration.hasMapper(boundType)) { // Spring may not know the real resource name so we set a flag // to prevent loading again this resource from the mapper interface // look at MapperAnnotationBuilder#loadXmlResource configuration.addLoadedResource("namespace:" + namespace); // 将当前接口类型添加到Configuration configuration.addMapper(boundType); } } } }
publicclassMapperFactoryBean<T> extendsSqlSessionDaoSupportimplementsFactoryBean<T> { // ... @Override public T getObject()throws Exception { return getSqlSession().getMapper(this.mapperInterface); } // ... }
publicclassSqlSessionTemplateimplementsSqlSession, DisposableBean{ // ... @Override public <T> T getMapper(Class<T> type){ return getConfiguration().getMapper(type, this); } // ... }
publicclassConfiguration{ // ... public <T> T getMapper(Class<T> type, SqlSession sqlSession){ return mapperRegistry.getMapper(type, sqlSession); } // ... }
publicclassMapperRegistry{ // ... public <T> T getMapper(Class<T> type, SqlSession sqlSession){ final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); if (mapperProxyFactory == null) { thrownew BindingException("Type " + type + " is not known to the MapperRegistry."); } try { return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { thrownew BindingException("Error getting mapper instance. Cause: " + e, e); } } // ... }
publicclassMapperProxyFactory<T> { @SuppressWarnings("unchecked") protected T newInstance(MapperProxy<T> mapperProxy){ return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); }
public T newInstance(SqlSession sqlSession){ final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } }
在上面例子中的 UserServiceImpl 是使用编程式事务处理的,当没有加上 @Transactional 时,在 int i = 1 / 0; 处发生异常时,上面已经插入的用户信息并不会被回滚,这就不符合我们日常的业务需求了。OK,从这里开始将要开始看看 Spring 是如何应用事务到我们的代码上的。
依赖结构
准备个需求
之前读其他无关数据库源码的时候,只是简单的依赖了 spring-boot-starter-web,现在由于需要数据库的参与,所以需要加上 MySQL 的驱动,以及一个最简单的 jdbc 框架。
在上面例子中的 UserServiceImpl 是使用编程式事务处理的,当没有加上 @Transactional 时,在 int i = 1 / 0; 处发生异常时,上面已经插入的用户信息并不会被回滚,这就不符合我们日常的业务需求了。OK,从这里开始将要开始看看 Spring 是如何应用事务到我们的代码上的。
/** * Indicate whether subclass-based (CGLIB) proxies are to be created ({@code true}) as * opposed to standard Java interface-based proxies ({@code false}). The default is * {@code false}. <strong>Applicable only if {@link #mode()} is set to * {@link AdviceMode#PROXY}</strong>. * <p>Note that setting this attribute to {@code true} will affect <em>all</em> * Spring-managed beans requiring proxying, not just those marked with * {@code@Transactional}. For example, other beans marked with Spring's * {@code@Async} annotation will be upgraded to subclass proxying at the same * time. This approach has no negative impact in practice unless one is explicitly * expecting one type of proxy vs another, e.g. in tests. */ booleanproxyTargetClass()defaultfalse;
/** * Indicate how transactional advice should be applied. * <p><b>The default is {@link AdviceMode#PROXY}.</b> * Please note that proxy mode allows for interception of calls through the proxy * only. Local calls within the same class cannot get intercepted that way; an * {@link Transactional} annotation on such a method within a local call will be * ignored since Spring's interceptor does not even kick in for such a runtime * scenario. For a more advanced mode of interception, consider switching this to * {@link AdviceMode#ASPECTJ}. */ AdviceMode mode()default AdviceMode.PROXY;
/** * Indicate the ordering of the execution of the transaction advisor * when multiple advices are applied at a specific joinpoint. * <p>The default is {@link Ordered#LOWEST_PRECEDENCE}. */ intorder()default Ordered.LOWEST_PRECEDENCE;
// META-INF/additional-spring-configuration-metadata.json { "name": "spring.aop.proxy-target-class", "type": "java.lang.Boolean", "description": "Whether subclass-based (CGLIB) proxies are to be created (true), as opposed to standard Java interface-based proxies (false).", "defaultValue": true },
// 在Callback这里即会织入BeanFactoryTransactionAttributeSourceAdvisor中的拦截器: // TransactionInterceptor Callback[] callbacks = getCallbacks(rootClass); Class<?>[] types = new Class<?>[callbacks.length]; for (int x = 0; x < types.length; x++) { types[x] = callbacks[x].getClass(); } // fixedInterceptorMap only populated at this point, after getCallbacks call above enhancer.setCallbackFilter(new ProxyCallbackFilter( this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset)); enhancer.setCallbackTypes(types);
// Generate the proxy class and create a proxy instance. return createProxyClassAndInstance(enhancer, callbacks); } catch (CodeGenerationException IllegalArgumentException ex) { thrownew AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() + ": Common causes of this problem include using a final class or a non-visible class", ex); } catch (Throwable ex) { // TargetSource.getTarget() failed thrownew AopConfigException("Unexpected AOP exception", ex); } }
// 响应式数据库操作 也是先跳过 if (vavrPresent && VavrDelegate.isVavrTry(retVal)) { // Set rollback-only in case of Vavr failure matching our rollback rules... TransactionStatus status = txInfo.getTransactionStatus(); if (status != null && txAttr != null) { retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status); } }
// 提交数据库事务 commitTransactionAfterReturning(txInfo); return retVal; } // CallbackPreferringPlatformTransactionManager 可回调的管理器 // 也是响应式的内容 else { final ThrowableHolder throwableHolder = new ThrowableHolder();
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in. try { Object result = ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> { TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status); try { Object retVal = invocation.proceedWithInvocation(); if (vavrPresent && VavrDelegate.isVavrTry(retVal)) { // Set rollback-only in case of Vavr failure matching our rollback rules... retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status); } return retVal; } catch (Throwable ex) { if (txAttr.rollbackOn(ex)) { // A RuntimeException: will lead to a rollback. if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } else { thrownew ThrowableHolderException(ex); } } else { // A normal return value: will lead to a commit. throwableHolder.throwable = ex; returnnull; } } finally { cleanupTransactionInfo(txInfo); } });
// Check result state: It might indicate a Throwable to rethrow. if (throwableHolder.throwable != null) { throw throwableHolder.throwable; } return result; } catch (ThrowableHolderException ex) { throw ex.getCause(); } catch (TransactionSystemException ex2) { if (throwableHolder.throwable != null) { logger.error("Application exception overridden by commit exception", throwableHolder.throwable); ex2.initApplicationException(throwableHolder.throwable); } throw ex2; } catch (Throwable ex2) { if (throwableHolder.throwable != null) { logger.error("Application exception overridden by commit exception", throwableHolder.throwable); } throw ex2; } } }
// 未提交读 int ISOLATION_READ_UNCOMMITTED = 1; // same as java.sql.Connection.TRANSACTION_READ_UNCOMMITTED;
// 不可重复读 int ISOLATION_READ_COMMITTED = 2; // same as java.sql.Connection.TRANSACTION_READ_COMMITTED;
// 可重复读 int ISOLATION_REPEATABLE_READ = 4; // same as java.sql.Connection.TRANSACTION_REPEATABLE_READ;
// 串行执行事务 int ISOLATION_SERIALIZABLE = 8; // same as java.sql.Connection.TRANSACTION_SERIALIZABLE;
/** * Use the default timeout of the underlying transaction system, * or none if timeouts are not supported. */ int TIMEOUT_DEFAULT = -1;
/** * Return the propagation behavior. * <p>Must return one of the {@code PROPAGATION_XXX} constants * defined on {@link TransactionDefinition this interface}. * <p>The default is {@link #PROPAGATION_REQUIRED}. * @return the propagation behavior * @see #PROPAGATION_REQUIRED * @see org.springframework.transaction.support.TransactionSynchronizationManager#isActualTransactionActive() */ defaultintgetPropagationBehavior(){ return PROPAGATION_REQUIRED; }
/** * Return the isolation level. * <p>Must return one of the {@code ISOLATION_XXX} constants defined on * {@link TransactionDefinition this interface}. Those constants are designed * to match the values of the same constants on {@link java.sql.Connection}. * <p>Exclusively designed for use with {@link #PROPAGATION_REQUIRED} or * {@link #PROPAGATION_REQUIRES_NEW} since it only applies to newly started * transactions. Consider switching the "validateExistingTransactions" flag to * "true" on your transaction manager if you'd like isolation level declarations * to get rejected when participating in an existing transaction with a different * isolation level. * <p>The default is {@link #ISOLATION_DEFAULT}. Note that a transaction manager * that does not support custom isolation levels will throw an exception when * given any other level than {@link #ISOLATION_DEFAULT}. * @return the isolation level * @see #ISOLATION_DEFAULT * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setValidateExistingTransaction */ defaultintgetIsolationLevel(){ return ISOLATION_DEFAULT; }
/** * Return the transaction timeout. * <p>Must return a number of seconds, or {@link #TIMEOUT_DEFAULT}. * <p>Exclusively designed for use with {@link #PROPAGATION_REQUIRED} or * {@link #PROPAGATION_REQUIRES_NEW} since it only applies to newly started * transactions. * <p>Note that a transaction manager that does not support timeouts will throw * an exception when given any other timeout than {@link #TIMEOUT_DEFAULT}. * <p>The default is {@link #TIMEOUT_DEFAULT}. * @return the transaction timeout */ defaultintgetTimeout(){ return TIMEOUT_DEFAULT; }
/** * Return whether to optimize as a read-only transaction. * <p>The read-only flag applies to any transaction context, whether backed * by an actual resource transaction ({@link #PROPAGATION_REQUIRED}/ * {@link #PROPAGATION_REQUIRES_NEW}) or operating non-transactionally at * the resource level ({@link #PROPAGATION_SUPPORTS}). In the latter case, * the flag will only apply to managed resources within the application, * such as a Hibernate {@code Session}. * <p>This just serves as a hint for the actual transaction subsystem; * it will <i>not necessarily</i> cause failure of write access attempts. * A transaction manager which cannot interpret the read-only hint will * <i>not</i> throw an exception when asked for a read-only transaction. * @return {@code true} if the transaction is to be optimized as read-only * ({@code false} by default) * @see org.springframework.transaction.support.TransactionSynchronization#beforeCommit(boolean) * @see org.springframework.transaction.support.TransactionSynchronizationManager#isCurrentTransactionReadOnly() */ defaultbooleanisReadOnly(){ returnfalse; }
/** * Return the name of this transaction. Can be {@code null}. * <p>This will be used as the transaction name to be shown in a * transaction monitor, if applicable (for example, WebLogic's). * <p>In case of Spring's declarative transactions, the exposed name will be * the {@code fully-qualified class name + "." + method name} (by default). * @return the name of this transaction ({@code null} by default} * @see org.springframework.transaction.interceptor.TransactionAspectSupport * @see org.springframework.transaction.support.TransactionSynchronizationManager#getCurrentTransactionName() */ @Nullable default String getName(){ returnnull; }
// Static builder methods
/** * Return an unmodifiable {@code TransactionDefinition} with defaults. * <p>For customization purposes, use the modifiable * {@link org.springframework.transaction.support.DefaultTransactionDefinition} * instead. * @since 5.2 */ static TransactionDefinition withDefaults(){ return StaticTransactionDefinition.INSTANCE; }
@Nullable protected TransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr){ // Do not attempt to lookup tx manager if no tx attributes are set if (txAttr == nullthis.beanFactory == null) { return getTransactionManager(); }
// 修改Connection的只读状态 if (con.getAutoCommit()) { txObject.setMustRestoreAutoCommit(true); if (logger.isDebugEnabled()) { logger.debug("Switching JDBC Connection [" + con + "] to manual commit"); } con.setAutoCommit(false); }
// 如果当前是只读事务则发送 SET TRANSACTION READ ONLY 给数据库切换事务状态 prepareTransactionalConnection(con, definition); txObject.getConnectionHolder().setTransactionActive(true);
int timeout = determineTimeout(definition); if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { txObject.getConnectionHolder().setTimeoutInSeconds(timeout); }
// 把ConnectionHolder绑定到TransactionSynchronizationManager if (txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder()); } }
catch (Throwable ex) { if (txObject.isNewConnectionHolder()) { DataSourceUtils.releaseConnection(con, obtainDataSource()); txObject.setConnectionHolder(null, false); } thrownew CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex); } }
@Override publicfinalvoidcommit(TransactionStatus status)throws TransactionException { if (status.isCompleted()) { thrownew IllegalTransactionStateException( "Transaction is already completed - do not call commit or rollback more than once per transaction"); }
// Throw UnexpectedRollbackException if we have a global rollback-only // marker but still didn't get a corresponding exception from commit. if (unexpectedRollback) { thrownew UnexpectedRollbackException( "Transaction silently rolled back because it has been marked as rollback-only"); } } catch (UnexpectedRollbackException ex) { // can only be caused by doCommit triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK); throw ex; } catch (TransactionException ex) { // can only be caused by doCommit if (isRollbackOnCommitFailure()) { doRollbackOnCommitException(status, ex); } else { triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); } throw ex; } catch (RuntimeException Error ex) { if (!beforeCompletionInvoked) { triggerBeforeCompletion(status); } doRollbackOnCommitException(status, ex); throw ex; }
// Trigger afterCommit callbacks, with an exception thrown there // propagated to callers but the transaction still considered as committed. try { // 调用事务生命周期的后处理器 triggerAfterCommit(status); } finally { triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED); }
} finally { cleanupAfterCompletion(status); } }
拿到 Connection 调用 Commit 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
@Override protectedvoiddoCommit(DefaultTransactionStatus status){ DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction(); Connection con = txObject.getConnectionHolder().getConnection(); if (status.isDebug()) { logger.debug("Committing JDBC transaction on Connection [" + con + "]"); } try { con.commit(); } catch (SQLException ex) { thrownew TransactionSystemException("Could not commit JDBC transaction", ex); } }
protectedvoidcompleteTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex){ if (txInfo != null && txInfo.getTransactionStatus() != null) { if (logger.isTraceEnabled()) { logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "] after exception: " + ex); } // 判断当前抛出的异常是否需要回滚数据库 if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) { try { txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus()); } catch (TransactionSystemException ex2) { logger.error("Application exception overridden by rollback exception", ex); ex2.initApplicationException(ex); throw ex2; } catch (RuntimeException Error ex2) { logger.error("Application exception overridden by rollback exception", ex); throw ex2; } } // 如果不会滚,继续提交 else { // We don't roll back on this exception. // Will still roll back if TransactionStatus.isRollbackOnly() is true. try { txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); } catch (TransactionSystemException ex2) { logger.error("Application exception overridden by commit exception", ex); ex2.initApplicationException(ex); throw ex2; } catch (RuntimeException Error ex2) { logger.error("Application exception overridden by commit exception", ex); throw ex2; } } } }
好吧来到了处理方法:
1 2 3 4 5 6 7 8 9 10
@Override publicfinalvoidrollback(TransactionStatus status)throws TransactionException { if (status.isCompleted()) { thrownew IllegalTransactionStateException( "Transaction is already completed - do not call commit or rollback more than once per transaction"); }
// Raise UnexpectedRollbackException if we had a global rollback-only marker if (unexpectedRollback) { thrownew UnexpectedRollbackException( "Transaction rolled back because it has been marked as rollback-only"); } } finally { cleanupAfterCompletion(status); } }
拿到 Connection 进行回滚。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
@Override protectedvoiddoRollback(DefaultTransactionStatus status){ DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction(); Connection con = txObject.getConnectionHolder().getConnection(); if (status.isDebug()) { logger.debug("Rolling back JDBC transaction on Connection [" + con + "]"); } try { con.rollback(); } catch (SQLException ex) { thrownew TransactionSystemException("Could not roll back JDBC transaction", ex); } }
/** * Indicate whether subclass-based (CGLIB) proxies are to be created ({@code true}) as * opposed to standard Java interface-based proxies ({@code false}). The default is * {@code false}. <strong>Applicable only if {@link #mode()} is set to * {@link AdviceMode#PROXY}</strong>. * <p>Note that setting this attribute to {@code true} will affect <em>all</em> * Spring-managed beans requiring proxying, not just those marked with * {@code@Transactional}. For example, other beans marked with Spring's * {@code@Async} annotation will be upgraded to subclass proxying at the same * time. This approach has no negative impact in practice unless one is explicitly * expecting one type of proxy vs another, e.g. in tests. */ booleanproxyTargetClass()defaultfalse;
/** * Indicate how transactional advice should be applied. * <p><b>The default is {@link AdviceMode#PROXY}.</b> * Please note that proxy mode allows for interception of calls through the proxy * only. Local calls within the same class cannot get intercepted that way; an * {@link Transactional} annotation on such a method within a local call will be * ignored since Spring's interceptor does not even kick in for such a runtime * scenario. For a more advanced mode of interception, consider switching this to * {@link AdviceMode#ASPECTJ}. */ AdviceMode mode()default AdviceMode.PROXY;
/** * Indicate the ordering of the execution of the transaction advisor * when multiple advices are applied at a specific joinpoint. * <p>The default is {@link Ordered#LOWEST_PRECEDENCE}. */ intorder()default Ordered.LOWEST_PRECEDENCE;
// META-INF/additional-spring-configuration-metadata.json { "name": "spring.aop.proxy-target-class", "type": "java.lang.Boolean", "description": "Whether subclass-based (CGLIB) proxies are to be created (true), as opposed to standard Java interface-based proxies (false).", "defaultValue": true },
然而搜遍谷歌 Spring 项目,只看到 Spring 成员说了一句:
准备个需求
之前读其他无关数据库源码的时候,只是简单的依赖了 spring-boot-starter-web,现在由于需要数据库的参与,所以需要加上 MySQL 的驱动,以及一个最简单的 jdbc 框架。
在上面例子中的 UserServiceImpl 是使用编程式事务处理的,当没有加上 @Transactional 时,在 int i = 1 / 0; 处发生异常时,上面已经插入的用户信息并不会被回滚,这就不符合我们日常的业务需求了。OK,从这里开始将要开始看看 Spring 是如何应用事务到我们的代码上的。
/** * Indicate whether subclass-based (CGLIB) proxies are to be created ({@code true}) as * opposed to standard Java interface-based proxies ({@code false}). The default is * {@code false}. <strong>Applicable only if {@link #mode()} is set to * {@link AdviceMode#PROXY}</strong>. * <p>Note that setting this attribute to {@code true} will affect <em>all</em> * Spring-managed beans requiring proxying, not just those marked with * {@code@Transactional}. For example, other beans marked with Spring's * {@code@Async} annotation will be upgraded to subclass proxying at the same * time. This approach has no negative impact in practice unless one is explicitly * expecting one type of proxy vs another, e.g. in tests. */ booleanproxyTargetClass()defaultfalse;
/** * Indicate how transactional advice should be applied. * <p><b>The default is {@link AdviceMode#PROXY}.</b> * Please note that proxy mode allows for interception of calls through the proxy * only. Local calls within the same class cannot get intercepted that way; an * {@link Transactional} annotation on such a method within a local call will be * ignored since Spring's interceptor does not even kick in for such a runtime * scenario. For a more advanced mode of interception, consider switching this to * {@link AdviceMode#ASPECTJ}. */ AdviceMode mode()default AdviceMode.PROXY;
/** * Indicate the ordering of the execution of the transaction advisor * when multiple advices are applied at a specific joinpoint. * <p>The default is {@link Ordered#LOWEST_PRECEDENCE}. */ intorder()default Ordered.LOWEST_PRECEDENCE;
// META-INF/additional-spring-configuration-metadata.json { "name": "spring.aop.proxy-target-class", "type": "java.lang.Boolean", "description": "Whether subclass-based (CGLIB) proxies are to be created (true), as opposed to standard Java interface-based proxies (false).", "defaultValue": true },
// 在Callback这里即会织入BeanFactoryTransactionAttributeSourceAdvisor中的拦截器: // TransactionInterceptor Callback[] callbacks = getCallbacks(rootClass); Class<?>[] types = new Class<?>[callbacks.length]; for (int x = 0; x < types.length; x++) { types[x] = callbacks[x].getClass(); } // fixedInterceptorMap only populated at this point, after getCallbacks call above enhancer.setCallbackFilter(new ProxyCallbackFilter( this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset)); enhancer.setCallbackTypes(types);
// Generate the proxy class and create a proxy instance. return createProxyClassAndInstance(enhancer, callbacks); } catch (CodeGenerationException IllegalArgumentException ex) { thrownew AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() + ": Common causes of this problem include using a final class or a non-visible class", ex); } catch (Throwable ex) { // TargetSource.getTarget() failed thrownew AopConfigException("Unexpected AOP exception", ex); } }
// 响应式数据库操作 也是先跳过 if (vavrPresent && VavrDelegate.isVavrTry(retVal)) { // Set rollback-only in case of Vavr failure matching our rollback rules... TransactionStatus status = txInfo.getTransactionStatus(); if (status != null && txAttr != null) { retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status); } }
// 提交数据库事务 commitTransactionAfterReturning(txInfo); return retVal; } // CallbackPreferringPlatformTransactionManager 可回调的管理器 // 也是响应式的内容 else { final ThrowableHolder throwableHolder = new ThrowableHolder();
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in. try { Object result = ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> { TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status); try { Object retVal = invocation.proceedWithInvocation(); if (vavrPresent && VavrDelegate.isVavrTry(retVal)) { // Set rollback-only in case of Vavr failure matching our rollback rules... retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status); } return retVal; } catch (Throwable ex) { if (txAttr.rollbackOn(ex)) { // A RuntimeException: will lead to a rollback. if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } else { thrownew ThrowableHolderException(ex); } } else { // A normal return value: will lead to a commit. throwableHolder.throwable = ex; returnnull; } } finally { cleanupTransactionInfo(txInfo); } });
// Check result state: It might indicate a Throwable to rethrow. if (throwableHolder.throwable != null) { throw throwableHolder.throwable; } return result; } catch (ThrowableHolderException ex) { throw ex.getCause(); } catch (TransactionSystemException ex2) { if (throwableHolder.throwable != null) { logger.error("Application exception overridden by commit exception", throwableHolder.throwable); ex2.initApplicationException(throwableHolder.throwable); } throw ex2; } catch (Throwable ex2) { if (throwableHolder.throwable != null) { logger.error("Application exception overridden by commit exception", throwableHolder.throwable); } throw ex2; } } }
// 未提交读 int ISOLATION_READ_UNCOMMITTED = 1; // same as java.sql.Connection.TRANSACTION_READ_UNCOMMITTED;
// 不可重复读 int ISOLATION_READ_COMMITTED = 2; // same as java.sql.Connection.TRANSACTION_READ_COMMITTED;
// 可重复读 int ISOLATION_REPEATABLE_READ = 4; // same as java.sql.Connection.TRANSACTION_REPEATABLE_READ;
// 串行执行事务 int ISOLATION_SERIALIZABLE = 8; // same as java.sql.Connection.TRANSACTION_SERIALIZABLE;
/** * Use the default timeout of the underlying transaction system, * or none if timeouts are not supported. */ int TIMEOUT_DEFAULT = -1;
/** * Return the propagation behavior. * <p>Must return one of the {@code PROPAGATION_XXX} constants * defined on {@link TransactionDefinition this interface}. * <p>The default is {@link #PROPAGATION_REQUIRED}. * @return the propagation behavior * @see #PROPAGATION_REQUIRED * @see org.springframework.transaction.support.TransactionSynchronizationManager#isActualTransactionActive() */ defaultintgetPropagationBehavior(){ return PROPAGATION_REQUIRED; }
/** * Return the isolation level. * <p>Must return one of the {@code ISOLATION_XXX} constants defined on * {@link TransactionDefinition this interface}. Those constants are designed * to match the values of the same constants on {@link java.sql.Connection}. * <p>Exclusively designed for use with {@link #PROPAGATION_REQUIRED} or * {@link #PROPAGATION_REQUIRES_NEW} since it only applies to newly started * transactions. Consider switching the "validateExistingTransactions" flag to * "true" on your transaction manager if you'd like isolation level declarations * to get rejected when participating in an existing transaction with a different * isolation level. * <p>The default is {@link #ISOLATION_DEFAULT}. Note that a transaction manager * that does not support custom isolation levels will throw an exception when * given any other level than {@link #ISOLATION_DEFAULT}. * @return the isolation level * @see #ISOLATION_DEFAULT * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setValidateExistingTransaction */ defaultintgetIsolationLevel(){ return ISOLATION_DEFAULT; }
/** * Return the transaction timeout. * <p>Must return a number of seconds, or {@link #TIMEOUT_DEFAULT}. * <p>Exclusively designed for use with {@link #PROPAGATION_REQUIRED} or * {@link #PROPAGATION_REQUIRES_NEW} since it only applies to newly started * transactions. * <p>Note that a transaction manager that does not support timeouts will throw * an exception when given any other timeout than {@link #TIMEOUT_DEFAULT}. * <p>The default is {@link #TIMEOUT_DEFAULT}. * @return the transaction timeout */ defaultintgetTimeout(){ return TIMEOUT_DEFAULT; }
/** * Return whether to optimize as a read-only transaction. * <p>The read-only flag applies to any transaction context, whether backed * by an actual resource transaction ({@link #PROPAGATION_REQUIRED}/ * {@link #PROPAGATION_REQUIRES_NEW}) or operating non-transactionally at * the resource level ({@link #PROPAGATION_SUPPORTS}). In the latter case, * the flag will only apply to managed resources within the application, * such as a Hibernate {@code Session}. * <p>This just serves as a hint for the actual transaction subsystem; * it will <i>not necessarily</i> cause failure of write access attempts. * A transaction manager which cannot interpret the read-only hint will * <i>not</i> throw an exception when asked for a read-only transaction. * @return {@code true} if the transaction is to be optimized as read-only * ({@code false} by default) * @see org.springframework.transaction.support.TransactionSynchronization#beforeCommit(boolean) * @see org.springframework.transaction.support.TransactionSynchronizationManager#isCurrentTransactionReadOnly() */ defaultbooleanisReadOnly(){ returnfalse; }
/** * Return the name of this transaction. Can be {@code null}. * <p>This will be used as the transaction name to be shown in a * transaction monitor, if applicable (for example, WebLogic's). * <p>In case of Spring's declarative transactions, the exposed name will be * the {@code fully-qualified class name + "." + method name} (by default). * @return the name of this transaction ({@code null} by default} * @see org.springframework.transaction.interceptor.TransactionAspectSupport * @see org.springframework.transaction.support.TransactionSynchronizationManager#getCurrentTransactionName() */ @Nullable default String getName(){ returnnull; }
// Static builder methods
/** * Return an unmodifiable {@code TransactionDefinition} with defaults. * <p>For customization purposes, use the modifiable * {@link org.springframework.transaction.support.DefaultTransactionDefinition} * instead. * @since 5.2 */ static TransactionDefinition withDefaults(){ return StaticTransactionDefinition.INSTANCE; }
@Nullable protected TransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr){ // Do not attempt to lookup tx manager if no tx attributes are set if (txAttr == nullthis.beanFactory == null) { return getTransactionManager(); }
// 修改Connection的只读状态 if (con.getAutoCommit()) { txObject.setMustRestoreAutoCommit(true); if (logger.isDebugEnabled()) { logger.debug("Switching JDBC Connection [" + con + "] to manual commit"); } con.setAutoCommit(false); }
// 如果当前是只读事务则发送 SET TRANSACTION READ ONLY 给数据库切换事务状态 prepareTransactionalConnection(con, definition); txObject.getConnectionHolder().setTransactionActive(true);
int timeout = determineTimeout(definition); if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { txObject.getConnectionHolder().setTimeoutInSeconds(timeout); }
// 把ConnectionHolder绑定到TransactionSynchronizationManager if (txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder()); } }
catch (Throwable ex) { if (txObject.isNewConnectionHolder()) { DataSourceUtils.releaseConnection(con, obtainDataSource()); txObject.setConnectionHolder(null, false); } thrownew CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex); } }
@Override publicfinalvoidcommit(TransactionStatus status)throws TransactionException { if (status.isCompleted()) { thrownew IllegalTransactionStateException( "Transaction is already completed - do not call commit or rollback more than once per transaction"); }
// Throw UnexpectedRollbackException if we have a global rollback-only // marker but still didn't get a corresponding exception from commit. if (unexpectedRollback) { thrownew UnexpectedRollbackException( "Transaction silently rolled back because it has been marked as rollback-only"); } } catch (UnexpectedRollbackException ex) { // can only be caused by doCommit triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK); throw ex; } catch (TransactionException ex) { // can only be caused by doCommit if (isRollbackOnCommitFailure()) { doRollbackOnCommitException(status, ex); } else { triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); } throw ex; } catch (RuntimeException Error ex) { if (!beforeCompletionInvoked) { triggerBeforeCompletion(status); } doRollbackOnCommitException(status, ex); throw ex; }
// Trigger afterCommit callbacks, with an exception thrown there // propagated to callers but the transaction still considered as committed. try { // 调用事务生命周期的后处理器 triggerAfterCommit(status); } finally { triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED); }
} finally { cleanupAfterCompletion(status); } }
拿到 Connection 调用 Commit 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
@Override protectedvoiddoCommit(DefaultTransactionStatus status){ DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction(); Connection con = txObject.getConnectionHolder().getConnection(); if (status.isDebug()) { logger.debug("Committing JDBC transaction on Connection [" + con + "]"); } try { con.commit(); } catch (SQLException ex) { thrownew TransactionSystemException("Could not commit JDBC transaction", ex); } }
protectedvoidcompleteTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex){ if (txInfo != null && txInfo.getTransactionStatus() != null) { if (logger.isTraceEnabled()) { logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "] after exception: " + ex); } // 判断当前抛出的异常是否需要回滚数据库 if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) { try { txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus()); } catch (TransactionSystemException ex2) { logger.error("Application exception overridden by rollback exception", ex); ex2.initApplicationException(ex); throw ex2; } catch (RuntimeException Error ex2) { logger.error("Application exception overridden by rollback exception", ex); throw ex2; } } // 如果不会滚,继续提交 else { // We don't roll back on this exception. // Will still roll back if TransactionStatus.isRollbackOnly() is true. try { txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); } catch (TransactionSystemException ex2) { logger.error("Application exception overridden by commit exception", ex); ex2.initApplicationException(ex); throw ex2; } catch (RuntimeException Error ex2) { logger.error("Application exception overridden by commit exception", ex); throw ex2; } } } }
好吧来到了处理方法:
1 2 3 4 5 6 7 8 9 10
@Override publicfinalvoidrollback(TransactionStatus status)throws TransactionException { if (status.isCompleted()) { thrownew IllegalTransactionStateException( "Transaction is already completed - do not call commit or rollback more than once per transaction"); }
// Raise UnexpectedRollbackException if we had a global rollback-only marker if (unexpectedRollback) { thrownew UnexpectedRollbackException( "Transaction rolled back because it has been marked as rollback-only"); } } finally { cleanupAfterCompletion(status); } }
拿到 Connection 进行回滚。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
@Override protectedvoiddoRollback(DefaultTransactionStatus status){ DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction(); Connection con = txObject.getConnectionHolder().getConnection(); if (status.isDebug()) { logger.debug("Rolling back JDBC transaction on Connection [" + con + "]"); } try { con.rollback(); } catch (SQLException ex) { thrownew TransactionSystemException("Could not roll back JDBC transaction", ex); } }
// 在Callback这里即会织入BeanFactoryTransactionAttributeSourceAdvisor中的拦截器: // TransactionInterceptor Callback[] callbacks = getCallbacks(rootClass); Class<?>[] types = new Class<?>[callbacks.length]; for (int x = 0; x < types.length; x++) { types[x] = callbacks[x].getClass(); } // fixedInterceptorMap only populated at this point, after getCallbacks call above enhancer.setCallbackFilter(new ProxyCallbackFilter( this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset)); enhancer.setCallbackTypes(types);
// Generate the proxy class and create a proxy instance. return createProxyClassAndInstance(enhancer, callbacks); } catch (CodeGenerationException IllegalArgumentException ex) { thrownew AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() + ": Common causes of this problem include using a final class or a non-visible class", ex); } catch (Throwable ex) { // TargetSource.getTarget() failed thrownew AopConfigException("Unexpected AOP exception", ex); } }
// 响应式数据库操作 也是先跳过 if (vavrPresent && VavrDelegate.isVavrTry(retVal)) { // Set rollback-only in case of Vavr failure matching our rollback rules... TransactionStatus status = txInfo.getTransactionStatus(); if (status != null && txAttr != null) { retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status); } }
// 提交数据库事务 commitTransactionAfterReturning(txInfo); return retVal; } // CallbackPreferringPlatformTransactionManager 可回调的管理器 // 也是响应式的内容 else { final ThrowableHolder throwableHolder = new ThrowableHolder();
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in. try { Object result = ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> { TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status); try { Object retVal = invocation.proceedWithInvocation(); if (vavrPresent && VavrDelegate.isVavrTry(retVal)) { // Set rollback-only in case of Vavr failure matching our rollback rules... retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status); } return retVal; } catch (Throwable ex) { if (txAttr.rollbackOn(ex)) { // A RuntimeException: will lead to a rollback. if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } else { thrownew ThrowableHolderException(ex); } } else { // A normal return value: will lead to a commit. throwableHolder.throwable = ex; returnnull; } } finally { cleanupTransactionInfo(txInfo); } });
// Check result state: It might indicate a Throwable to rethrow. if (throwableHolder.throwable != null) { throw throwableHolder.throwable; } return result; } catch (ThrowableHolderException ex) { throw ex.getCause(); } catch (TransactionSystemException ex2) { if (throwableHolder.throwable != null) { logger.error("Application exception overridden by commit exception", throwableHolder.throwable); ex2.initApplicationException(throwableHolder.throwable); } throw ex2; } catch (Throwable ex2) { if (throwableHolder.throwable != null) { logger.error("Application exception overridden by commit exception", throwableHolder.throwable); } throw ex2; } } }
在上面例子中的 UserServiceImpl 是使用编程式事务处理的,当没有加上 @Transactional 时,在 int i = 1 / 0; 处发生异常时,上面已经插入的用户信息并不会被回滚,这就不符合我们日常的业务需求了。OK,从这里开始将要开始看看 Spring 是如何应用事务到我们的代码上的。
/** * Indicate whether subclass-based (CGLIB) proxies are to be created ({@code true}) as * opposed to standard Java interface-based proxies ({@code false}). The default is * {@code false}. <strong>Applicable only if {@link #mode()} is set to * {@link AdviceMode#PROXY}</strong>. * <p>Note that setting this attribute to {@code true} will affect <em>all</em> * Spring-managed beans requiring proxying, not just those marked with * {@code@Transactional}. For example, other beans marked with Spring's * {@code@Async} annotation will be upgraded to subclass proxying at the same * time. This approach has no negative impact in practice unless one is explicitly * expecting one type of proxy vs another, e.g. in tests. */ booleanproxyTargetClass()defaultfalse;
/** * Indicate how transactional advice should be applied. * <p><b>The default is {@link AdviceMode#PROXY}.</b> * Please note that proxy mode allows for interception of calls through the proxy * only. Local calls within the same class cannot get intercepted that way; an * {@link Transactional} annotation on such a method within a local call will be * ignored since Spring's interceptor does not even kick in for such a runtime * scenario. For a more advanced mode of interception, consider switching this to * {@link AdviceMode#ASPECTJ}. */ AdviceMode mode()default AdviceMode.PROXY;
/** * Indicate the ordering of the execution of the transaction advisor * when multiple advices are applied at a specific joinpoint. * <p>The default is {@link Ordered#LOWEST_PRECEDENCE}. */ intorder()default Ordered.LOWEST_PRECEDENCE;
// META-INF/additional-spring-configuration-metadata.json { "name": "spring.aop.proxy-target-class", "type": "java.lang.Boolean", "description": "Whether subclass-based (CGLIB) proxies are to be created (true), as opposed to standard Java interface-based proxies (false).", "defaultValue": true },
// 在Callback这里即会织入BeanFactoryTransactionAttributeSourceAdvisor中的拦截器: // TransactionInterceptor Callback[] callbacks = getCallbacks(rootClass); Class<?>[] types = new Class<?>[callbacks.length]; for (int x = 0; x < types.length; x++) { types[x] = callbacks[x].getClass(); } // fixedInterceptorMap only populated at this point, after getCallbacks call above enhancer.setCallbackFilter(new ProxyCallbackFilter( this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset)); enhancer.setCallbackTypes(types);
// Generate the proxy class and create a proxy instance. return createProxyClassAndInstance(enhancer, callbacks); } catch (CodeGenerationException IllegalArgumentException ex) { thrownew AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() + ": Common causes of this problem include using a final class or a non-visible class", ex); } catch (Throwable ex) { // TargetSource.getTarget() failed thrownew AopConfigException("Unexpected AOP exception", ex); } }
// 响应式数据库操作 也是先跳过 if (vavrPresent && VavrDelegate.isVavrTry(retVal)) { // Set rollback-only in case of Vavr failure matching our rollback rules... TransactionStatus status = txInfo.getTransactionStatus(); if (status != null && txAttr != null) { retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status); } }
// 提交数据库事务 commitTransactionAfterReturning(txInfo); return retVal; } // CallbackPreferringPlatformTransactionManager 可回调的管理器 // 也是响应式的内容 else { final ThrowableHolder throwableHolder = new ThrowableHolder();
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in. try { Object result = ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> { TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status); try { Object retVal = invocation.proceedWithInvocation(); if (vavrPresent && VavrDelegate.isVavrTry(retVal)) { // Set rollback-only in case of Vavr failure matching our rollback rules... retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status); } return retVal; } catch (Throwable ex) { if (txAttr.rollbackOn(ex)) { // A RuntimeException: will lead to a rollback. if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } else { thrownew ThrowableHolderException(ex); } } else { // A normal return value: will lead to a commit. throwableHolder.throwable = ex; returnnull; } } finally { cleanupTransactionInfo(txInfo); } });
// Check result state: It might indicate a Throwable to rethrow. if (throwableHolder.throwable != null) { throw throwableHolder.throwable; } return result; } catch (ThrowableHolderException ex) { throw ex.getCause(); } catch (TransactionSystemException ex2) { if (throwableHolder.throwable != null) { logger.error("Application exception overridden by commit exception", throwableHolder.throwable); ex2.initApplicationException(throwableHolder.throwable); } throw ex2; } catch (Throwable ex2) { if (throwableHolder.throwable != null) { logger.error("Application exception overridden by commit exception", throwableHolder.throwable); } throw ex2; } } }
// 未提交读 int ISOLATION_READ_UNCOMMITTED = 1; // same as java.sql.Connection.TRANSACTION_READ_UNCOMMITTED;
// 不可重复读 int ISOLATION_READ_COMMITTED = 2; // same as java.sql.Connection.TRANSACTION_READ_COMMITTED;
// 可重复读 int ISOLATION_REPEATABLE_READ = 4; // same as java.sql.Connection.TRANSACTION_REPEATABLE_READ;
// 串行执行事务 int ISOLATION_SERIALIZABLE = 8; // same as java.sql.Connection.TRANSACTION_SERIALIZABLE;
/** * Use the default timeout of the underlying transaction system, * or none if timeouts are not supported. */ int TIMEOUT_DEFAULT = -1;
/** * Return the propagation behavior. * <p>Must return one of the {@code PROPAGATION_XXX} constants * defined on {@link TransactionDefinition this interface}. * <p>The default is {@link #PROPAGATION_REQUIRED}. * @return the propagation behavior * @see #PROPAGATION_REQUIRED * @see org.springframework.transaction.support.TransactionSynchronizationManager#isActualTransactionActive() */ defaultintgetPropagationBehavior(){ return PROPAGATION_REQUIRED; }
/** * Return the isolation level. * <p>Must return one of the {@code ISOLATION_XXX} constants defined on * {@link TransactionDefinition this interface}. Those constants are designed * to match the values of the same constants on {@link java.sql.Connection}. * <p>Exclusively designed for use with {@link #PROPAGATION_REQUIRED} or * {@link #PROPAGATION_REQUIRES_NEW} since it only applies to newly started * transactions. Consider switching the "validateExistingTransactions" flag to * "true" on your transaction manager if you'd like isolation level declarations * to get rejected when participating in an existing transaction with a different * isolation level. * <p>The default is {@link #ISOLATION_DEFAULT}. Note that a transaction manager * that does not support custom isolation levels will throw an exception when * given any other level than {@link #ISOLATION_DEFAULT}. * @return the isolation level * @see #ISOLATION_DEFAULT * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setValidateExistingTransaction */ defaultintgetIsolationLevel(){ return ISOLATION_DEFAULT; }
/** * Return the transaction timeout. * <p>Must return a number of seconds, or {@link #TIMEOUT_DEFAULT}. * <p>Exclusively designed for use with {@link #PROPAGATION_REQUIRED} or * {@link #PROPAGATION_REQUIRES_NEW} since it only applies to newly started * transactions. * <p>Note that a transaction manager that does not support timeouts will throw * an exception when given any other timeout than {@link #TIMEOUT_DEFAULT}. * <p>The default is {@link #TIMEOUT_DEFAULT}. * @return the transaction timeout */ defaultintgetTimeout(){ return TIMEOUT_DEFAULT; }
/** * Return whether to optimize as a read-only transaction. * <p>The read-only flag applies to any transaction context, whether backed * by an actual resource transaction ({@link #PROPAGATION_REQUIRED}/ * {@link #PROPAGATION_REQUIRES_NEW}) or operating non-transactionally at * the resource level ({@link #PROPAGATION_SUPPORTS}). In the latter case, * the flag will only apply to managed resources within the application, * such as a Hibernate {@code Session}. * <p>This just serves as a hint for the actual transaction subsystem; * it will <i>not necessarily</i> cause failure of write access attempts. * A transaction manager which cannot interpret the read-only hint will * <i>not</i> throw an exception when asked for a read-only transaction. * @return {@code true} if the transaction is to be optimized as read-only * ({@code false} by default) * @see org.springframework.transaction.support.TransactionSynchronization#beforeCommit(boolean) * @see org.springframework.transaction.support.TransactionSynchronizationManager#isCurrentTransactionReadOnly() */ defaultbooleanisReadOnly(){ returnfalse; }
/** * Return the name of this transaction. Can be {@code null}. * <p>This will be used as the transaction name to be shown in a * transaction monitor, if applicable (for example, WebLogic's). * <p>In case of Spring's declarative transactions, the exposed name will be * the {@code fully-qualified class name + "." + method name} (by default). * @return the name of this transaction ({@code null} by default} * @see org.springframework.transaction.interceptor.TransactionAspectSupport * @see org.springframework.transaction.support.TransactionSynchronizationManager#getCurrentTransactionName() */ @Nullable default String getName(){ returnnull; }
// Static builder methods
/** * Return an unmodifiable {@code TransactionDefinition} with defaults. * <p>For customization purposes, use the modifiable * {@link org.springframework.transaction.support.DefaultTransactionDefinition} * instead. * @since 5.2 */ static TransactionDefinition withDefaults(){ return StaticTransactionDefinition.INSTANCE; }
@Nullable protected TransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr){ // Do not attempt to lookup tx manager if no tx attributes are set if (txAttr == nullthis.beanFactory == null) { return getTransactionManager(); }
// 修改Connection的只读状态 if (con.getAutoCommit()) { txObject.setMustRestoreAutoCommit(true); if (logger.isDebugEnabled()) { logger.debug("Switching JDBC Connection [" + con + "] to manual commit"); } con.setAutoCommit(false); }
// 如果当前是只读事务则发送 SET TRANSACTION READ ONLY 给数据库切换事务状态 prepareTransactionalConnection(con, definition); txObject.getConnectionHolder().setTransactionActive(true);
int timeout = determineTimeout(definition); if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { txObject.getConnectionHolder().setTimeoutInSeconds(timeout); }
// 把ConnectionHolder绑定到TransactionSynchronizationManager if (txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder()); } }
catch (Throwable ex) { if (txObject.isNewConnectionHolder()) { DataSourceUtils.releaseConnection(con, obtainDataSource()); txObject.setConnectionHolder(null, false); } thrownew CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex); } }
@Override publicfinalvoidcommit(TransactionStatus status)throws TransactionException { if (status.isCompleted()) { thrownew IllegalTransactionStateException( "Transaction is already completed - do not call commit or rollback more than once per transaction"); }
// Throw UnexpectedRollbackException if we have a global rollback-only // marker but still didn't get a corresponding exception from commit. if (unexpectedRollback) { thrownew UnexpectedRollbackException( "Transaction silently rolled back because it has been marked as rollback-only"); } } catch (UnexpectedRollbackException ex) { // can only be caused by doCommit triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK); throw ex; } catch (TransactionException ex) { // can only be caused by doCommit if (isRollbackOnCommitFailure()) { doRollbackOnCommitException(status, ex); } else { triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); } throw ex; } catch (RuntimeException Error ex) { if (!beforeCompletionInvoked) { triggerBeforeCompletion(status); } doRollbackOnCommitException(status, ex); throw ex; }
// Trigger afterCommit callbacks, with an exception thrown there // propagated to callers but the transaction still considered as committed. try { // 调用事务生命周期的后处理器 triggerAfterCommit(status); } finally { triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED); }
} finally { cleanupAfterCompletion(status); } }
拿到 Connection 调用 Commit 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
@Override protectedvoiddoCommit(DefaultTransactionStatus status){ DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction(); Connection con = txObject.getConnectionHolder().getConnection(); if (status.isDebug()) { logger.debug("Committing JDBC transaction on Connection [" + con + "]"); } try { con.commit(); } catch (SQLException ex) { thrownew TransactionSystemException("Could not commit JDBC transaction", ex); } }
protectedvoidcompleteTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex){ if (txInfo != null && txInfo.getTransactionStatus() != null) { if (logger.isTraceEnabled()) { logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "] after exception: " + ex); } // 判断当前抛出的异常是否需要回滚数据库 if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) { try { txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus()); } catch (TransactionSystemException ex2) { logger.error("Application exception overridden by rollback exception", ex); ex2.initApplicationException(ex); throw ex2; } catch (RuntimeException Error ex2) { logger.error("Application exception overridden by rollback exception", ex); throw ex2; } } // 如果不会滚,继续提交 else { // We don't roll back on this exception. // Will still roll back if TransactionStatus.isRollbackOnly() is true. try { txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); } catch (TransactionSystemException ex2) { logger.error("Application exception overridden by commit exception", ex); ex2.initApplicationException(ex); throw ex2; } catch (RuntimeException Error ex2) { logger.error("Application exception overridden by commit exception", ex); throw ex2; } } } }
好吧来到了处理方法:
1 2 3 4 5 6 7 8 9 10
@Override publicfinalvoidrollback(TransactionStatus status)throws TransactionException { if (status.isCompleted()) { thrownew IllegalTransactionStateException( "Transaction is already completed - do not call commit or rollback more than once per transaction"); }
// Raise UnexpectedRollbackException if we had a global rollback-only marker if (unexpectedRollback) { thrownew UnexpectedRollbackException( "Transaction rolled back because it has been marked as rollback-only"); } } finally { cleanupAfterCompletion(status); } }
拿到 Connection 进行回滚。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
@Override protectedvoiddoRollback(DefaultTransactionStatus status){ DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction(); Connection con = txObject.getConnectionHolder().getConnection(); if (status.isDebug()) { logger.debug("Rolling back JDBC transaction on Connection [" + con + "]"); } try { con.rollback(); } catch (SQLException ex) { thrownew TransactionSystemException("Could not roll back JDBC transaction", ex); } }
// 未提交读 int ISOLATION_READ_UNCOMMITTED = 1; // same as java.sql.Connection.TRANSACTION_READ_UNCOMMITTED;
// 不可重复读 int ISOLATION_READ_COMMITTED = 2; // same as java.sql.Connection.TRANSACTION_READ_COMMITTED;
// 可重复读 int ISOLATION_REPEATABLE_READ = 4; // same as java.sql.Connection.TRANSACTION_REPEATABLE_READ;
// 串行执行事务 int ISOLATION_SERIALIZABLE = 8; // same as java.sql.Connection.TRANSACTION_SERIALIZABLE;
/** * Use the default timeout of the underlying transaction system, * or none if timeouts are not supported. */ int TIMEOUT_DEFAULT = -1;
/** * Return the propagation behavior. * <p>Must return one of the {@code PROPAGATION_XXX} constants * defined on {@link TransactionDefinition this interface}. * <p>The default is {@link #PROPAGATION_REQUIRED}. * @return the propagation behavior * @see #PROPAGATION_REQUIRED * @see org.springframework.transaction.support.TransactionSynchronizationManager#isActualTransactionActive() */ defaultintgetPropagationBehavior(){ return PROPAGATION_REQUIRED; }
/** * Return the isolation level. * <p>Must return one of the {@code ISOLATION_XXX} constants defined on * {@link TransactionDefinition this interface}. Those constants are designed * to match the values of the same constants on {@link java.sql.Connection}. * <p>Exclusively designed for use with {@link #PROPAGATION_REQUIRED} or * {@link #PROPAGATION_REQUIRES_NEW} since it only applies to newly started * transactions. Consider switching the "validateExistingTransactions" flag to * "true" on your transaction manager if you'd like isolation level declarations * to get rejected when participating in an existing transaction with a different * isolation level. * <p>The default is {@link #ISOLATION_DEFAULT}. Note that a transaction manager * that does not support custom isolation levels will throw an exception when * given any other level than {@link #ISOLATION_DEFAULT}. * @return the isolation level * @see #ISOLATION_DEFAULT * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setValidateExistingTransaction */ defaultintgetIsolationLevel(){ return ISOLATION_DEFAULT; }
/** * Return the transaction timeout. * <p>Must return a number of seconds, or {@link #TIMEOUT_DEFAULT}. * <p>Exclusively designed for use with {@link #PROPAGATION_REQUIRED} or * {@link #PROPAGATION_REQUIRES_NEW} since it only applies to newly started * transactions. * <p>Note that a transaction manager that does not support timeouts will throw * an exception when given any other timeout than {@link #TIMEOUT_DEFAULT}. * <p>The default is {@link #TIMEOUT_DEFAULT}. * @return the transaction timeout */ defaultintgetTimeout(){ return TIMEOUT_DEFAULT; }
/** * Return whether to optimize as a read-only transaction. * <p>The read-only flag applies to any transaction context, whether backed * by an actual resource transaction ({@link #PROPAGATION_REQUIRED}/ * {@link #PROPAGATION_REQUIRES_NEW}) or operating non-transactionally at * the resource level ({@link #PROPAGATION_SUPPORTS}). In the latter case, * the flag will only apply to managed resources within the application, * such as a Hibernate {@code Session}. * <p>This just serves as a hint for the actual transaction subsystem; * it will <i>not necessarily</i> cause failure of write access attempts. * A transaction manager which cannot interpret the read-only hint will * <i>not</i> throw an exception when asked for a read-only transaction. * @return {@code true} if the transaction is to be optimized as read-only * ({@code false} by default) * @see org.springframework.transaction.support.TransactionSynchronization#beforeCommit(boolean) * @see org.springframework.transaction.support.TransactionSynchronizationManager#isCurrentTransactionReadOnly() */ defaultbooleanisReadOnly(){ returnfalse; }
/** * Return the name of this transaction. Can be {@code null}. * <p>This will be used as the transaction name to be shown in a * transaction monitor, if applicable (for example, WebLogic's). * <p>In case of Spring's declarative transactions, the exposed name will be * the {@code fully-qualified class name + "." + method name} (by default). * @return the name of this transaction ({@code null} by default} * @see org.springframework.transaction.interceptor.TransactionAspectSupport * @see org.springframework.transaction.support.TransactionSynchronizationManager#getCurrentTransactionName() */ @Nullable default String getName(){ returnnull; }
// Static builder methods
/** * Return an unmodifiable {@code TransactionDefinition} with defaults. * <p>For customization purposes, use the modifiable * {@link org.springframework.transaction.support.DefaultTransactionDefinition} * instead. * @since 5.2 */ static TransactionDefinition withDefaults(){ return StaticTransactionDefinition.INSTANCE; }
@Nullable protected TransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr){ // Do not attempt to lookup tx manager if no tx attributes are set if (txAttr == nullthis.beanFactory == null) { return getTransactionManager(); }
// 修改Connection的只读状态 if (con.getAutoCommit()) { txObject.setMustRestoreAutoCommit(true); if (logger.isDebugEnabled()) { logger.debug("Switching JDBC Connection [" + con + "] to manual commit"); } con.setAutoCommit(false); }
// 如果当前是只读事务则发送 SET TRANSACTION READ ONLY 给数据库切换事务状态 prepareTransactionalConnection(con, definition); txObject.getConnectionHolder().setTransactionActive(true);
int timeout = determineTimeout(definition); if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { txObject.getConnectionHolder().setTimeoutInSeconds(timeout); }
// 把ConnectionHolder绑定到TransactionSynchronizationManager if (txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder()); } }
catch (Throwable ex) { if (txObject.isNewConnectionHolder()) { DataSourceUtils.releaseConnection(con, obtainDataSource()); txObject.setConnectionHolder(null, false); } thrownew CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex); } }
@Override publicfinalvoidcommit(TransactionStatus status)throws TransactionException { if (status.isCompleted()) { thrownew IllegalTransactionStateException( "Transaction is already completed - do not call commit or rollback more than once per transaction"); }
// Throw UnexpectedRollbackException if we have a global rollback-only // marker but still didn't get a corresponding exception from commit. if (unexpectedRollback) { thrownew UnexpectedRollbackException( "Transaction silently rolled back because it has been marked as rollback-only"); } } catch (UnexpectedRollbackException ex) { // can only be caused by doCommit triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK); throw ex; } catch (TransactionException ex) { // can only be caused by doCommit if (isRollbackOnCommitFailure()) { doRollbackOnCommitException(status, ex); } else { triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); } throw ex; } catch (RuntimeException Error ex) { if (!beforeCompletionInvoked) { triggerBeforeCompletion(status); } doRollbackOnCommitException(status, ex); throw ex; }
// Trigger afterCommit callbacks, with an exception thrown there // propagated to callers but the transaction still considered as committed. try { // 调用事务生命周期的后处理器 triggerAfterCommit(status); } finally { triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED); }
} finally { cleanupAfterCompletion(status); } }
拿到 Connection 调用 Commit 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
@Override protectedvoiddoCommit(DefaultTransactionStatus status){ DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction(); Connection con = txObject.getConnectionHolder().getConnection(); if (status.isDebug()) { logger.debug("Committing JDBC transaction on Connection [" + con + "]"); } try { con.commit(); } catch (SQLException ex) { thrownew TransactionSystemException("Could not commit JDBC transaction", ex); } }
protectedvoidcompleteTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex){ if (txInfo != null && txInfo.getTransactionStatus() != null) { if (logger.isTraceEnabled()) { logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "] after exception: " + ex); } // 判断当前抛出的异常是否需要回滚数据库 if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) { try { txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus()); } catch (TransactionSystemException ex2) { logger.error("Application exception overridden by rollback exception", ex); ex2.initApplicationException(ex); throw ex2; } catch (RuntimeException Error ex2) { logger.error("Application exception overridden by rollback exception", ex); throw ex2; } } // 如果不会滚,继续提交 else { // We don't roll back on this exception. // Will still roll back if TransactionStatus.isRollbackOnly() is true. try { txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); } catch (TransactionSystemException ex2) { logger.error("Application exception overridden by commit exception", ex); ex2.initApplicationException(ex); throw ex2; } catch (RuntimeException Error ex2) { logger.error("Application exception overridden by commit exception", ex); throw ex2; } } } }
好吧来到了处理方法:
1 2 3 4 5 6 7 8 9 10
@Override publicfinalvoidrollback(TransactionStatus status)throws TransactionException { if (status.isCompleted()) { thrownew IllegalTransactionStateException( "Transaction is already completed - do not call commit or rollback more than once per transaction"); }
// Raise UnexpectedRollbackException if we had a global rollback-only marker if (unexpectedRollback) { thrownew UnexpectedRollbackException( "Transaction rolled back because it has been marked as rollback-only"); } } finally { cleanupAfterCompletion(status); } }
拿到 Connection 进行回滚。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
@Override protectedvoiddoRollback(DefaultTransactionStatus status){ DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction(); Connection con = txObject.getConnectionHolder().getConnection(); if (status.isDebug()) { logger.debug("Rolling back JDBC transaction on Connection [" + con + "]"); } try { con.rollback(); } catch (SQLException ex) { thrownew TransactionSystemException("Could not roll back JDBC transaction", ex); } }
这时候获取了一个 ServletWebServerFactory 的 BeanDefinition,然后使用这个 Factory 来创建我们目前的 Web 环境。
1 2 3 4 5 6 7 8 9 10 11 12 13
protected ServletWebServerFactory getWebServerFactory(){ // Use bean names so that we don't consider the hierarchy String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class); if (beanNames.length == 0) { thrownew ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing " + "ServletWebServerFactory bean."); } if (beanNames.length > 1) { thrownew ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple " + "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames)); } return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class); }
/* * The bean name for a DispatcherServlet that will be mapped to the root URL "/" */ publicstaticfinal String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
/* * The bean name for a ServletRegistrationBean for the DispatcherServlet "/" */ publicstaticfinal String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
@Bean @ConditionalOnBean(MultipartResolver.class) @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) public MultipartResolver multipartResolver(MultipartResolver resolver){ // Detect if the user has created a MultipartResolver but named it incorrectly return resolver; }
if (logger.isDebugEnabled()) { String value = this.enableLoggingRequestDetails ? "shown which may lead to unsafe logging of potentially sensitive data" : "masked to prevent unsafe logging of potentially sensitive data"; logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails + "': request parameters and headers will be " + value); }
if (this.webApplicationContext != null) { // 当前rootContext已经存在了 wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; // 已经激活,这里并不会进来 if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { // No context instance was injected at construction time -> see if one // has been registered in the servlet context. If one exists, it is assumed // that the parent context (if any) has already been set and that the // user has performed any initialization such as setting the context id wac = findWebApplicationContext(); } if (wac == null) { // No context instance is defined for this servlet -> create a local one wac = createWebApplicationContext(rootContext); }
if (this.publishContext) { // Publish the context as a servlet context attribute. String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); }
return wac; }
刷新上下文在上面那么多个容器中已经很有感觉了,就是刷新配置的 Bean 调用对应的构造器 AOP 注入啊啊巴拉巴拉。 所以,onRefresh 是整个 Web 容器初始化的关键:
这时候获取了一个 ServletWebServerFactory 的 BeanDefinition,然后使用这个 Factory 来创建我们目前的 Web 环境。
1 2 3 4 5 6 7 8 9 10 11 12 13
protected ServletWebServerFactory getWebServerFactory(){ // Use bean names so that we don't consider the hierarchy String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class); if (beanNames.length == 0) { thrownew ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing " + "ServletWebServerFactory bean."); } if (beanNames.length > 1) { thrownew ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple " + "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames)); } return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class); }
/* * The bean name for a DispatcherServlet that will be mapped to the root URL "/" */ publicstaticfinal String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
/* * The bean name for a ServletRegistrationBean for the DispatcherServlet "/" */ publicstaticfinal String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
@Bean @ConditionalOnBean(MultipartResolver.class) @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) public MultipartResolver multipartResolver(MultipartResolver resolver){ // Detect if the user has created a MultipartResolver but named it incorrectly return resolver; }
if (logger.isDebugEnabled()) { String value = this.enableLoggingRequestDetails ? "shown which may lead to unsafe logging of potentially sensitive data" : "masked to prevent unsafe logging of potentially sensitive data"; logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails + "': request parameters and headers will be " + value); }
if (this.webApplicationContext != null) { // 当前rootContext已经存在了 wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; // 已经激活,这里并不会进来 if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { // No context instance was injected at construction time -> see if one // has been registered in the servlet context. If one exists, it is assumed // that the parent context (if any) has already been set and that the // user has performed any initialization such as setting the context id wac = findWebApplicationContext(); } if (wac == null) { // No context instance is defined for this servlet -> create a local one wac = createWebApplicationContext(rootContext); }
if (this.publishContext) { // Publish the context as a servlet context attribute. String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); }
return wac; }
刷新上下文在上面那么多个容器中已经很有感觉了,就是刷新配置的 Bean 调用对应的构造器 AOP 注入啊啊巴拉巴拉。 所以,onRefresh 是整个 Web 容器初始化的关键:
if (this.detectAllHandlerMappings) { // Find all HandlerMappings in the ApplicationContext, including ancestor contexts. Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { // 把 handlerMappings 缓存在 DispatcherServlet this.handlerMappings = new ArrayList<>(matchingBeans.values()); // We keep HandlerMappings in sorted order. AnnotationAwareOrderComparator.sort(this.handlerMappings); } } else { try { HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default HandlerMapping later. } }
// Ensure we have at least one HandlerMapping, by registering // a default HandlerMapping if no other mappings are found. if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isTraceEnabled()) { logger.trace("No HandlerMappings declared for servlet '" + getServletName() + "': using default strategies from DispatcherServlet.properties"); } } }
initHandlerAdapters 也差不多代码就不贴上来了。
处理HTTP请求准备
那 HTTPServlet 处理请求是在 service 方法中处理的,所以现在我们需要先进入核心的这个方法中去看: FrameworkServlet 即这个 HTTPServlet 的第一个实现:
/** * Delegate GET requests to processRequest/doService. * <p>Will also be invoked by HttpServlet's default implementation of {@code doHead}, * with a {@code NoBodyResponse} that just captures the content length. * @see #doService * @see #doHead */ @Override protectedfinalvoiddoGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 保存flashMap if (this.flashMapManager != null) { FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); if (inputFlashMap != null) { request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); } request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); }
try { // 开始查找执行的控制器 doDispatch(request, response); } finally { if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Restore the original attribute snapshot, in case of an include. if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } } }
if (asyncManager.isConcurrentHandlingStarted()) { return; }
// 处理视图,但是目前是JSON,是个空处理 applyDefaultViewName(processedRequest, mv); // 执行其他的拦截器周期 mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { // As of 4.3, we're processing Errors thrown from handler methods as well, // making them available for @ExceptionHandler methods and other scenarios. dispatchException = new NestedServletException("Handler dispatch failed", err); } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err)); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
目前的拦截器链有四个:
RequestMappingHandlerAdapter;
HandlerFunctionAdapter;
HttpRequestHandlerAdapter;
SimpleControllerHandlerAdapter
在查找的过程中,返回第一个匹配的适配器:
1 2 3 4 5 6 7 8 9 10 11
protected HandlerAdapter getHandlerAdapter(Object handler)throws ServletException { if (this.handlerAdapters != null) { for (HandlerAdapter adapter : this.handlerAdapters) { if (adapter.supports(handler)) { return adapter; } } } thrownew ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); }
if (this.detectAllHandlerMappings) { // Find all HandlerMappings in the ApplicationContext, including ancestor contexts. Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { // 把 handlerMappings 缓存在 DispatcherServlet this.handlerMappings = new ArrayList<>(matchingBeans.values()); // We keep HandlerMappings in sorted order. AnnotationAwareOrderComparator.sort(this.handlerMappings); } } else { try { HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default HandlerMapping later. } }
// Ensure we have at least one HandlerMapping, by registering // a default HandlerMapping if no other mappings are found. if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isTraceEnabled()) { logger.trace("No HandlerMappings declared for servlet '" + getServletName() + "': using default strategies from DispatcherServlet.properties"); } } }
initHandlerAdapters 也差不多代码就不贴上来了。
处理HTTP请求准备
那 HTTPServlet 处理请求是在 service 方法中处理的,所以现在我们需要先进入核心的这个方法中去看: FrameworkServlet 即这个 HTTPServlet 的第一个实现:
/** * Delegate GET requests to processRequest/doService. * <p>Will also be invoked by HttpServlet's default implementation of {@code doHead}, * with a {@code NoBodyResponse} that just captures the content length. * @see #doService * @see #doHead */ @Override protectedfinalvoiddoGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 保存flashMap if (this.flashMapManager != null) { FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); if (inputFlashMap != null) { request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); } request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); }
try { // 开始查找执行的控制器 doDispatch(request, response); } finally { if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Restore the original attribute snapshot, in case of an include. if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } } }
if (asyncManager.isConcurrentHandlingStarted()) { return; }
// 处理视图,但是目前是JSON,是个空处理 applyDefaultViewName(processedRequest, mv); // 执行其他的拦截器周期 mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { // As of 4.3, we're processing Errors thrown from handler methods as well, // making them available for @ExceptionHandler methods and other scenarios. dispatchException = new NestedServletException("Handler dispatch failed", err); } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err)); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
目前的拦截器链有四个:
RequestMappingHandlerAdapter;
HandlerFunctionAdapter;
HttpRequestHandlerAdapter;
SimpleControllerHandlerAdapter
在查找的过程中,返回第一个匹配的适配器:
1 2 3 4 5 6 7 8 9 10 11
protected HandlerAdapter getHandlerAdapter(Object handler)throws ServletException { if (this.handlerAdapters != null) { for (HandlerAdapter adapter : this.handlerAdapters) { if (adapter.supports(handler)) { return adapter; } } } thrownew ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); }
这时候获取了一个 ServletWebServerFactory 的 BeanDefinition,然后使用这个 Factory 来创建我们目前的 Web 环境。
1 2 3 4 5 6 7 8 9 10 11 12 13
protected ServletWebServerFactory getWebServerFactory(){ // Use bean names so that we don't consider the hierarchy String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class); if (beanNames.length == 0) { thrownew ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing " + "ServletWebServerFactory bean."); } if (beanNames.length > 1) { thrownew ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple " + "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames)); } return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class); }
/* * The bean name for a DispatcherServlet that will be mapped to the root URL "/" */ publicstaticfinal String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
/* * The bean name for a ServletRegistrationBean for the DispatcherServlet "/" */ publicstaticfinal String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
@Bean @ConditionalOnBean(MultipartResolver.class) @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) public MultipartResolver multipartResolver(MultipartResolver resolver){ // Detect if the user has created a MultipartResolver but named it incorrectly return resolver; }
if (logger.isDebugEnabled()) { String value = this.enableLoggingRequestDetails ? "shown which may lead to unsafe logging of potentially sensitive data" : "masked to prevent unsafe logging of potentially sensitive data"; logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails + "': request parameters and headers will be " + value); }
if (this.webApplicationContext != null) { // 当前rootContext已经存在了 wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; // 已经激活,这里并不会进来 if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { // No context instance was injected at construction time -> see if one // has been registered in the servlet context. If one exists, it is assumed // that the parent context (if any) has already been set and that the // user has performed any initialization such as setting the context id wac = findWebApplicationContext(); } if (wac == null) { // No context instance is defined for this servlet -> create a local one wac = createWebApplicationContext(rootContext); }
if (this.publishContext) { // Publish the context as a servlet context attribute. String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); }
return wac; }
刷新上下文在上面那么多个容器中已经很有感觉了,就是刷新配置的 Bean 调用对应的构造器 AOP 注入啊啊巴拉巴拉。 所以,onRefresh 是整个 Web 容器初始化的关键:
if (this.detectAllHandlerMappings) { // Find all HandlerMappings in the ApplicationContext, including ancestor contexts. Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { // 把 handlerMappings 缓存在 DispatcherServlet this.handlerMappings = new ArrayList<>(matchingBeans.values()); // We keep HandlerMappings in sorted order. AnnotationAwareOrderComparator.sort(this.handlerMappings); } } else { try { HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default HandlerMapping later. } }
// Ensure we have at least one HandlerMapping, by registering // a default HandlerMapping if no other mappings are found. if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isTraceEnabled()) { logger.trace("No HandlerMappings declared for servlet '" + getServletName() + "': using default strategies from DispatcherServlet.properties"); } } }
initHandlerAdapters 也差不多代码就不贴上来了。
处理HTTP请求准备
那 HTTPServlet 处理请求是在 service 方法中处理的,所以现在我们需要先进入核心的这个方法中去看: FrameworkServlet 即这个 HTTPServlet 的第一个实现:
/** * Delegate GET requests to processRequest/doService. * <p>Will also be invoked by HttpServlet's default implementation of {@code doHead}, * with a {@code NoBodyResponse} that just captures the content length. * @see #doService * @see #doHead */ @Override protectedfinalvoiddoGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 保存flashMap if (this.flashMapManager != null) { FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); if (inputFlashMap != null) { request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); } request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); }
try { // 开始查找执行的控制器 doDispatch(request, response); } finally { if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Restore the original attribute snapshot, in case of an include. if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } } }
if (asyncManager.isConcurrentHandlingStarted()) { return; }
// 处理视图,但是目前是JSON,是个空处理 applyDefaultViewName(processedRequest, mv); // 执行其他的拦截器周期 mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { // As of 4.3, we're processing Errors thrown from handler methods as well, // making them available for @ExceptionHandler methods and other scenarios. dispatchException = new NestedServletException("Handler dispatch failed", err); } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err)); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
目前的拦截器链有四个:
RequestMappingHandlerAdapter;
HandlerFunctionAdapter;
HttpRequestHandlerAdapter;
SimpleControllerHandlerAdapter
在查找的过程中,返回第一个匹配的适配器:
1 2 3 4 5 6 7 8 9 10 11
protected HandlerAdapter getHandlerAdapter(Object handler)throws ServletException { if (this.handlerAdapters != null) { for (HandlerAdapter adapter : this.handlerAdapters) { if (adapter.supports(handler)) { return adapter; } } } thrownew ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); }
其中,mybatis-spring-boot-autoconfigure 比较惹人注目,所以我们现在就看看这个项目。 首先我们看看 main/resources/META-INF/spring.factories,因为 Spring 很喜欢通过这些 meta 文件来促使各个模块很好的解耦但又能彼此配合工作,所以这个文件是定义 自动装配 开始的工厂类:
1 2 3 4
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\ org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
通过静态方法,传递一个 Class 类作为主资源,然后再传递给主资源数组的 run 方法。那么这个数组其实我们是可以传递多个主资源的,比如我们做项目的时候,想要每个模块包彼此分离,即可传递多个包的主资源路径。 new SpringApplication(primarySources).run(args); 才是真正的进入容器的准备阶段: 首先看看构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 14
publicSpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources){ this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); // 初始化主资源链表,用于下面需要读取的时候可以遍历. this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 判断Web环境,有webFlux/web/普通三个环境,主要通过类路径是否带有相对应需要的类来判断,如果都没有则初始化为普通Java项目. this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 初始化环境,这个接口多用于web环境,因为需要从web上下文加载一些信息. setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 初始化监听器,监听容器生命周期中需要回调的函数 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 从运行堆栈中寻找运行这个类的main方法所在的类 this.mainApplicationClass = deduceMainApplicationClass(); }
// Destroy already created singletons to avoid dangling resources. destroyBeans();
// Reset 'active' flag. cancelRefresh(ex);
// Propagate exception to caller. throw ex; }
finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor) if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } }
@Override publicvoidpostProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry){ int registryId = System.identityHashCode(registry); if (this.registriesPostProcessed.contains(registryId)) { thrownew IllegalStateException( "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); } if (this.factoriesPostProcessed.contains(registryId)) { thrownew IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + registry); } this.registriesPostProcessed.add(registryId);
/** * Build and validate a configuration model based on the registry of * {@link Configuration} classes. */ publicvoidprocessConfigBeanDefinitions(BeanDefinitionRegistry registry){ List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) { if (logger.isDebugEnabled()) { logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); } } elseif (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } }
// Return immediately if no @Configuration classes were found if (configCandidates.isEmpty()) { return; }
// Sort by previously determined @Order value, if applicable configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2); });
// Detect any custom bean name generation strategy supplied through the enclosing application context SingletonBeanRegistry sbr = null; if (registry instanceof SingletonBeanRegistry) { sbr = (SingletonBeanRegistry) registry; if (!this.localBeanNameGeneratorSet) { BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton( AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR); if (generator != null) { this.componentScanBeanNameGenerator = generator; this.importBeanNameGenerator = generator; } } }
if (this.environment == null) { this.environment = new StandardEnvironment(); }
// Parse each @Configuration class ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); // ↑ 这上面属于解析开始的准备工作 ↑ do { parser.parse(candidates); parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses);
candidates.clear(); if (registry.getBeanDefinitionCount() > candidateNames.length) { String[] newCandidateNames = registry.getBeanDefinitionNames(); Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); Set<String> alreadyParsedClasses = new HashSet<>(); for (ConfigurationClass configurationClass : alreadyParsed) { alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); } for (String candidateName : newCandidateNames) { if (!oldCandidateNames.contains(candidateName)) { BeanDefinition bd = registry.getBeanDefinition(candidateName); if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) { candidates.add(new BeanDefinitionHolder(bd, candidateName)); } } } candidateNames = newCandidateNames; } } while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); }
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) { // Clear cache in externally provided MetadataReaderFactory; this is a no-op // for a shared cache since it'll be cleared by the ApplicationContext. ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); } }
// 判断是否已经解析过了 ConfigurationClass existingClass = this.configurationClasses.get(configClass); if (existingClass != null) { if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } // Otherwise ignore new imported config class; existing non-imported class overrides it. return; } else { // Explicit bean definition found, probably replacing an import. // Let's remove the old one and go with the new one. this.configurationClasses.remove(configClass); this.knownSuperclasses.values().removeIf(configClass::equals); } }
// 递归解析配置类以及他的父类、接口等等 SourceClass sourceClass = asSourceClass(configClass); do { sourceClass = doProcessConfigurationClass(configClass, sourceClass); } while (sourceClass != null);
if (configClass.getMetadata().isAnnotated(Component.class.getName())) { // Recursively process any member (nested) classes first processMemberClasses(configClass, sourceClass); }
// 解析需要导入Property配置文件的类 for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); } else { logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } }
// 解析 @ComponentScan Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // The config class is annotated with @ComponentScan -> perform the scan immediately Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if needed for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } }
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass){ ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
其中,mybatis-spring-boot-autoconfigure 比较惹人注目,所以我们现在就看看这个项目。 首先我们看看 main/resources/META-INF/spring.factories,因为 Spring 很喜欢通过这些 meta 文件来促使各个模块很好的解耦但又能彼此配合工作,所以这个文件是定义 自动装配 开始的工厂类:
1 2 3 4
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\ org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
其中,mybatis-spring-boot-autoconfigure 比较惹人注目,所以我们现在就看看这个项目。 首先我们看看 main/resources/META-INF/spring.factories,因为 Spring 很喜欢通过这些 meta 文件来促使各个模块很好的解耦但又能彼此配合工作,所以这个文件是定义 自动装配 开始的工厂类:
1 2 3 4
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\ org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
通过静态方法,传递一个 Class 类作为主资源,然后再传递给主资源数组的 run 方法。那么这个数组其实我们是可以传递多个主资源的,比如我们做项目的时候,想要每个模块包彼此分离,即可传递多个包的主资源路径。 new SpringApplication(primarySources).run(args); 才是真正的进入容器的准备阶段: 首先看看构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 14
publicSpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources){ this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); // 初始化主资源链表,用于下面需要读取的时候可以遍历. this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 判断Web环境,有webFlux/web/普通三个环境,主要通过类路径是否带有相对应需要的类来判断,如果都没有则初始化为普通Java项目. this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 初始化环境,这个接口多用于web环境,因为需要从web上下文加载一些信息. setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 初始化监听器,监听容器生命周期中需要回调的函数 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 从运行堆栈中寻找运行这个类的main方法所在的类 this.mainApplicationClass = deduceMainApplicationClass(); }
// Destroy already created singletons to avoid dangling resources. destroyBeans();
// Reset 'active' flag. cancelRefresh(ex);
// Propagate exception to caller. throw ex; }
finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor) if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } }
@Override publicvoidpostProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry){ int registryId = System.identityHashCode(registry); if (this.registriesPostProcessed.contains(registryId)) { thrownew IllegalStateException( "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); } if (this.factoriesPostProcessed.contains(registryId)) { thrownew IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + registry); } this.registriesPostProcessed.add(registryId);
/** * Build and validate a configuration model based on the registry of * {@link Configuration} classes. */ publicvoidprocessConfigBeanDefinitions(BeanDefinitionRegistry registry){ List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) { if (logger.isDebugEnabled()) { logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); } } elseif (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } }
// Return immediately if no @Configuration classes were found if (configCandidates.isEmpty()) { return; }
// Sort by previously determined @Order value, if applicable configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2); });
// Detect any custom bean name generation strategy supplied through the enclosing application context SingletonBeanRegistry sbr = null; if (registry instanceof SingletonBeanRegistry) { sbr = (SingletonBeanRegistry) registry; if (!this.localBeanNameGeneratorSet) { BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton( AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR); if (generator != null) { this.componentScanBeanNameGenerator = generator; this.importBeanNameGenerator = generator; } } }
if (this.environment == null) { this.environment = new StandardEnvironment(); }
// Parse each @Configuration class ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); // ↑ 这上面属于解析开始的准备工作 ↑ do { parser.parse(candidates); parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses);
candidates.clear(); if (registry.getBeanDefinitionCount() > candidateNames.length) { String[] newCandidateNames = registry.getBeanDefinitionNames(); Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); Set<String> alreadyParsedClasses = new HashSet<>(); for (ConfigurationClass configurationClass : alreadyParsed) { alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); } for (String candidateName : newCandidateNames) { if (!oldCandidateNames.contains(candidateName)) { BeanDefinition bd = registry.getBeanDefinition(candidateName); if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) { candidates.add(new BeanDefinitionHolder(bd, candidateName)); } } } candidateNames = newCandidateNames; } } while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); }
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) { // Clear cache in externally provided MetadataReaderFactory; this is a no-op // for a shared cache since it'll be cleared by the ApplicationContext. ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); } }
// 判断是否已经解析过了 ConfigurationClass existingClass = this.configurationClasses.get(configClass); if (existingClass != null) { if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } // Otherwise ignore new imported config class; existing non-imported class overrides it. return; } else { // Explicit bean definition found, probably replacing an import. // Let's remove the old one and go with the new one. this.configurationClasses.remove(configClass); this.knownSuperclasses.values().removeIf(configClass::equals); } }
// 递归解析配置类以及他的父类、接口等等 SourceClass sourceClass = asSourceClass(configClass); do { sourceClass = doProcessConfigurationClass(configClass, sourceClass); } while (sourceClass != null);
if (configClass.getMetadata().isAnnotated(Component.class.getName())) { // Recursively process any member (nested) classes first processMemberClasses(configClass, sourceClass); }
// 解析需要导入Property配置文件的类 for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); } else { logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } }
// 解析 @ComponentScan Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // The config class is annotated with @ComponentScan -> perform the scan immediately Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if needed for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } }
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass){ ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
通过静态方法,传递一个 Class 类作为主资源,然后再传递给主资源数组的 run 方法。那么这个数组其实我们是可以传递多个主资源的,比如我们做项目的时候,想要每个模块包彼此分离,即可传递多个包的主资源路径。 new SpringApplication(primarySources).run(args); 才是真正的进入容器的准备阶段: 首先看看构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 14
publicSpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources){ this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); // 初始化主资源链表,用于下面需要读取的时候可以遍历. this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 判断Web环境,有webFlux/web/普通三个环境,主要通过类路径是否带有相对应需要的类来判断,如果都没有则初始化为普通Java项目. this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 初始化环境,这个接口多用于web环境,因为需要从web上下文加载一些信息. setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 初始化监听器,监听容器生命周期中需要回调的函数 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 从运行堆栈中寻找运行这个类的main方法所在的类 this.mainApplicationClass = deduceMainApplicationClass(); }
// Destroy already created singletons to avoid dangling resources. destroyBeans();
// Reset 'active' flag. cancelRefresh(ex);
// Propagate exception to caller. throw ex; }
finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor) if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } }
@Override publicvoidpostProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry){ int registryId = System.identityHashCode(registry); if (this.registriesPostProcessed.contains(registryId)) { thrownew IllegalStateException( "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); } if (this.factoriesPostProcessed.contains(registryId)) { thrownew IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + registry); } this.registriesPostProcessed.add(registryId);
/** * Build and validate a configuration model based on the registry of * {@link Configuration} classes. */ publicvoidprocessConfigBeanDefinitions(BeanDefinitionRegistry registry){ List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) { if (logger.isDebugEnabled()) { logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); } } elseif (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } }
// Return immediately if no @Configuration classes were found if (configCandidates.isEmpty()) { return; }
// Sort by previously determined @Order value, if applicable configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2); });
// Detect any custom bean name generation strategy supplied through the enclosing application context SingletonBeanRegistry sbr = null; if (registry instanceof SingletonBeanRegistry) { sbr = (SingletonBeanRegistry) registry; if (!this.localBeanNameGeneratorSet) { BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton( AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR); if (generator != null) { this.componentScanBeanNameGenerator = generator; this.importBeanNameGenerator = generator; } } }
if (this.environment == null) { this.environment = new StandardEnvironment(); }
// Parse each @Configuration class ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); // ↑ 这上面属于解析开始的准备工作 ↑ do { parser.parse(candidates); parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses);
candidates.clear(); if (registry.getBeanDefinitionCount() > candidateNames.length) { String[] newCandidateNames = registry.getBeanDefinitionNames(); Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); Set<String> alreadyParsedClasses = new HashSet<>(); for (ConfigurationClass configurationClass : alreadyParsed) { alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); } for (String candidateName : newCandidateNames) { if (!oldCandidateNames.contains(candidateName)) { BeanDefinition bd = registry.getBeanDefinition(candidateName); if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) { candidates.add(new BeanDefinitionHolder(bd, candidateName)); } } } candidateNames = newCandidateNames; } } while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); }
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) { // Clear cache in externally provided MetadataReaderFactory; this is a no-op // for a shared cache since it'll be cleared by the ApplicationContext. ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); } }
// 判断是否已经解析过了 ConfigurationClass existingClass = this.configurationClasses.get(configClass); if (existingClass != null) { if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } // Otherwise ignore new imported config class; existing non-imported class overrides it. return; } else { // Explicit bean definition found, probably replacing an import. // Let's remove the old one and go with the new one. this.configurationClasses.remove(configClass); this.knownSuperclasses.values().removeIf(configClass::equals); } }
// 递归解析配置类以及他的父类、接口等等 SourceClass sourceClass = asSourceClass(configClass); do { sourceClass = doProcessConfigurationClass(configClass, sourceClass); } while (sourceClass != null);
if (configClass.getMetadata().isAnnotated(Component.class.getName())) { // Recursively process any member (nested) classes first processMemberClasses(configClass, sourceClass); }
// 解析需要导入Property配置文件的类 for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); } else { logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } }
// 解析 @ComponentScan Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // The config class is annotated with @ComponentScan -> perform the scan immediately Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if needed for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } }
其中,mybatis-spring-boot-autoconfigure 比较惹人注目,所以我们现在就看看这个项目。 首先我们看看 main/resources/META-INF/spring.factories,因为 Spring 很喜欢通过这些 meta 文件来促使各个模块很好的解耦但又能彼此配合工作,所以这个文件是定义 自动装配 开始的工厂类:
1 2 3 4
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\ org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
通过静态方法,传递一个 Class 类作为主资源,然后再传递给主资源数组的 run 方法。那么这个数组其实我们是可以传递多个主资源的,比如我们做项目的时候,想要每个模块包彼此分离,即可传递多个包的主资源路径。 new SpringApplication(primarySources).run(args); 才是真正的进入容器的准备阶段: 首先看看构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 14
publicSpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources){ this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); // 初始化主资源链表,用于下面需要读取的时候可以遍历. this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 判断Web环境,有webFlux/web/普通三个环境,主要通过类路径是否带有相对应需要的类来判断,如果都没有则初始化为普通Java项目. this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 初始化环境,这个接口多用于web环境,因为需要从web上下文加载一些信息. setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 初始化监听器,监听容器生命周期中需要回调的函数 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 从运行堆栈中寻找运行这个类的main方法所在的类 this.mainApplicationClass = deduceMainApplicationClass(); }
// Destroy already created singletons to avoid dangling resources. destroyBeans();
// Reset 'active' flag. cancelRefresh(ex);
// Propagate exception to caller. throw ex; }
finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor) if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } }
@Override publicvoidpostProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry){ int registryId = System.identityHashCode(registry); if (this.registriesPostProcessed.contains(registryId)) { thrownew IllegalStateException( "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); } if (this.factoriesPostProcessed.contains(registryId)) { thrownew IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + registry); } this.registriesPostProcessed.add(registryId);
/** * Build and validate a configuration model based on the registry of * {@link Configuration} classes. */ publicvoidprocessConfigBeanDefinitions(BeanDefinitionRegistry registry){ List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) { if (logger.isDebugEnabled()) { logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); } } elseif (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } }
// Return immediately if no @Configuration classes were found if (configCandidates.isEmpty()) { return; }
// Sort by previously determined @Order value, if applicable configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2); });
// Detect any custom bean name generation strategy supplied through the enclosing application context SingletonBeanRegistry sbr = null; if (registry instanceof SingletonBeanRegistry) { sbr = (SingletonBeanRegistry) registry; if (!this.localBeanNameGeneratorSet) { BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton( AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR); if (generator != null) { this.componentScanBeanNameGenerator = generator; this.importBeanNameGenerator = generator; } } }
if (this.environment == null) { this.environment = new StandardEnvironment(); }
// Parse each @Configuration class ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); // ↑ 这上面属于解析开始的准备工作 ↑ do { parser.parse(candidates); parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses);
candidates.clear(); if (registry.getBeanDefinitionCount() > candidateNames.length) { String[] newCandidateNames = registry.getBeanDefinitionNames(); Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); Set<String> alreadyParsedClasses = new HashSet<>(); for (ConfigurationClass configurationClass : alreadyParsed) { alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); } for (String candidateName : newCandidateNames) { if (!oldCandidateNames.contains(candidateName)) { BeanDefinition bd = registry.getBeanDefinition(candidateName); if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) { candidates.add(new BeanDefinitionHolder(bd, candidateName)); } } } candidateNames = newCandidateNames; } } while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); }
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) { // Clear cache in externally provided MetadataReaderFactory; this is a no-op // for a shared cache since it'll be cleared by the ApplicationContext. ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); } }
// 判断是否已经解析过了 ConfigurationClass existingClass = this.configurationClasses.get(configClass); if (existingClass != null) { if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } // Otherwise ignore new imported config class; existing non-imported class overrides it. return; } else { // Explicit bean definition found, probably replacing an import. // Let's remove the old one and go with the new one. this.configurationClasses.remove(configClass); this.knownSuperclasses.values().removeIf(configClass::equals); } }
// 递归解析配置类以及他的父类、接口等等 SourceClass sourceClass = asSourceClass(configClass); do { sourceClass = doProcessConfigurationClass(configClass, sourceClass); } while (sourceClass != null);
if (configClass.getMetadata().isAnnotated(Component.class.getName())) { // Recursively process any member (nested) classes first processMemberClasses(configClass, sourceClass); }
// 解析需要导入Property配置文件的类 for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); } else { logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } }
// 解析 @ComponentScan Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // The config class is annotated with @ComponentScan -> perform the scan immediately Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if needed for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } }
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass){ ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
其中,mybatis-spring-boot-autoconfigure 比较惹人注目,所以我们现在就看看这个项目。 首先我们看看 main/resources/META-INF/spring.factories,因为 Spring 很喜欢通过这些 meta 文件来促使各个模块很好的解耦但又能彼此配合工作,所以这个文件是定义 自动装配 开始的工厂类:
1 2 3 4
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\ org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
通过静态方法,传递一个 Class 类作为主资源,然后再传递给主资源数组的 run 方法。那么这个数组其实我们是可以传递多个主资源的,比如我们做项目的时候,想要每个模块包彼此分离,即可传递多个包的主资源路径。 new SpringApplication(primarySources).run(args); 才是真正的进入容器的准备阶段: 首先看看构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 14
publicSpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources){ this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); // 初始化主资源链表,用于下面需要读取的时候可以遍历. this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 判断Web环境,有webFlux/web/普通三个环境,主要通过类路径是否带有相对应需要的类来判断,如果都没有则初始化为普通Java项目. this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 初始化环境,这个接口多用于web环境,因为需要从web上下文加载一些信息. setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 初始化监听器,监听容器生命周期中需要回调的函数 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 从运行堆栈中寻找运行这个类的main方法所在的类 this.mainApplicationClass = deduceMainApplicationClass(); }
// Destroy already created singletons to avoid dangling resources. destroyBeans();
// Reset 'active' flag. cancelRefresh(ex);
// Propagate exception to caller. throw ex; }
finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor) if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } }
@Override publicvoidpostProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry){ int registryId = System.identityHashCode(registry); if (this.registriesPostProcessed.contains(registryId)) { thrownew IllegalStateException( "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); } if (this.factoriesPostProcessed.contains(registryId)) { thrownew IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + registry); } this.registriesPostProcessed.add(registryId);
/** * Build and validate a configuration model based on the registry of * {@link Configuration} classes. */ publicvoidprocessConfigBeanDefinitions(BeanDefinitionRegistry registry){ List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) { if (logger.isDebugEnabled()) { logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); } } elseif (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } }
// Return immediately if no @Configuration classes were found if (configCandidates.isEmpty()) { return; }
// Sort by previously determined @Order value, if applicable configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2); });
// Detect any custom bean name generation strategy supplied through the enclosing application context SingletonBeanRegistry sbr = null; if (registry instanceof SingletonBeanRegistry) { sbr = (SingletonBeanRegistry) registry; if (!this.localBeanNameGeneratorSet) { BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton( AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR); if (generator != null) { this.componentScanBeanNameGenerator = generator; this.importBeanNameGenerator = generator; } } }
if (this.environment == null) { this.environment = new StandardEnvironment(); }
// Parse each @Configuration class ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); // ↑ 这上面属于解析开始的准备工作 ↑ do { parser.parse(candidates); parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses);
candidates.clear(); if (registry.getBeanDefinitionCount() > candidateNames.length) { String[] newCandidateNames = registry.getBeanDefinitionNames(); Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); Set<String> alreadyParsedClasses = new HashSet<>(); for (ConfigurationClass configurationClass : alreadyParsed) { alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); } for (String candidateName : newCandidateNames) { if (!oldCandidateNames.contains(candidateName)) { BeanDefinition bd = registry.getBeanDefinition(candidateName); if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) { candidates.add(new BeanDefinitionHolder(bd, candidateName)); } } } candidateNames = newCandidateNames; } } while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); }
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) { // Clear cache in externally provided MetadataReaderFactory; this is a no-op // for a shared cache since it'll be cleared by the ApplicationContext. ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); } }
// 判断是否已经解析过了 ConfigurationClass existingClass = this.configurationClasses.get(configClass); if (existingClass != null) { if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } // Otherwise ignore new imported config class; existing non-imported class overrides it. return; } else { // Explicit bean definition found, probably replacing an import. // Let's remove the old one and go with the new one. this.configurationClasses.remove(configClass); this.knownSuperclasses.values().removeIf(configClass::equals); } }
// 递归解析配置类以及他的父类、接口等等 SourceClass sourceClass = asSourceClass(configClass); do { sourceClass = doProcessConfigurationClass(configClass, sourceClass); } while (sourceClass != null);
if (configClass.getMetadata().isAnnotated(Component.class.getName())) { // Recursively process any member (nested) classes first processMemberClasses(configClass, sourceClass); }
// 解析需要导入Property配置文件的类 for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); } else { logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } }
// 解析 @ComponentScan Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // The config class is annotated with @ComponentScan -> perform the scan immediately Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if needed for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } }
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass){ ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
其中,mybatis-spring-boot-autoconfigure 比较惹人注目,所以我们现在就看看这个项目。 首先我们看看 main/resources/META-INF/spring.factories,因为 Spring 很喜欢通过这些 meta 文件来促使各个模块很好的解耦但又能彼此配合工作,所以这个文件是定义 自动装配 开始的工厂类:
1 2 3 4
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\ org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
通过静态方法,传递一个 Class 类作为主资源,然后再传递给主资源数组的 run 方法。那么这个数组其实我们是可以传递多个主资源的,比如我们做项目的时候,想要每个模块包彼此分离,即可传递多个包的主资源路径。 new SpringApplication(primarySources).run(args); 才是真正的进入容器的准备阶段: 首先看看构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 14
publicSpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources){ this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); // 初始化主资源链表,用于下面需要读取的时候可以遍历. this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 判断Web环境,有webFlux/web/普通三个环境,主要通过类路径是否带有相对应需要的类来判断,如果都没有则初始化为普通Java项目. this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 初始化环境,这个接口多用于web环境,因为需要从web上下文加载一些信息. setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 初始化监听器,监听容器生命周期中需要回调的函数 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 从运行堆栈中寻找运行这个类的main方法所在的类 this.mainApplicationClass = deduceMainApplicationClass(); }
// Destroy already created singletons to avoid dangling resources. destroyBeans();
// Reset 'active' flag. cancelRefresh(ex);
// Propagate exception to caller. throw ex; }
finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor) if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } }
@Override publicvoidpostProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry){ int registryId = System.identityHashCode(registry); if (this.registriesPostProcessed.contains(registryId)) { thrownew IllegalStateException( "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); } if (this.factoriesPostProcessed.contains(registryId)) { thrownew IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + registry); } this.registriesPostProcessed.add(registryId);
/** * Build and validate a configuration model based on the registry of * {@link Configuration} classes. */ publicvoidprocessConfigBeanDefinitions(BeanDefinitionRegistry registry){ List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) { if (logger.isDebugEnabled()) { logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); } } elseif (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } }
// Return immediately if no @Configuration classes were found if (configCandidates.isEmpty()) { return; }
// Sort by previously determined @Order value, if applicable configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2); });
// Detect any custom bean name generation strategy supplied through the enclosing application context SingletonBeanRegistry sbr = null; if (registry instanceof SingletonBeanRegistry) { sbr = (SingletonBeanRegistry) registry; if (!this.localBeanNameGeneratorSet) { BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton( AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR); if (generator != null) { this.componentScanBeanNameGenerator = generator; this.importBeanNameGenerator = generator; } } }
if (this.environment == null) { this.environment = new StandardEnvironment(); }
// Parse each @Configuration class ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); // ↑ 这上面属于解析开始的准备工作 ↑ do { parser.parse(candidates); parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses);
candidates.clear(); if (registry.getBeanDefinitionCount() > candidateNames.length) { String[] newCandidateNames = registry.getBeanDefinitionNames(); Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); Set<String> alreadyParsedClasses = new HashSet<>(); for (ConfigurationClass configurationClass : alreadyParsed) { alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); } for (String candidateName : newCandidateNames) { if (!oldCandidateNames.contains(candidateName)) { BeanDefinition bd = registry.getBeanDefinition(candidateName); if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) { candidates.add(new BeanDefinitionHolder(bd, candidateName)); } } } candidateNames = newCandidateNames; } } while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); }
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) { // Clear cache in externally provided MetadataReaderFactory; this is a no-op // for a shared cache since it'll be cleared by the ApplicationContext. ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); } }
// 判断是否已经解析过了 ConfigurationClass existingClass = this.configurationClasses.get(configClass); if (existingClass != null) { if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } // Otherwise ignore new imported config class; existing non-imported class overrides it. return; } else { // Explicit bean definition found, probably replacing an import. // Let's remove the old one and go with the new one. this.configurationClasses.remove(configClass); this.knownSuperclasses.values().removeIf(configClass::equals); } }
// 递归解析配置类以及他的父类、接口等等 SourceClass sourceClass = asSourceClass(configClass); do { sourceClass = doProcessConfigurationClass(configClass, sourceClass); } while (sourceClass != null);
if (configClass.getMetadata().isAnnotated(Component.class.getName())) { // Recursively process any member (nested) classes first processMemberClasses(configClass, sourceClass); }
// 解析需要导入Property配置文件的类 for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); } else { logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } }
// 解析 @ComponentScan Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // The config class is annotated with @ComponentScan -> perform the scan immediately Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if needed for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } }
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass){ ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
其中,mybatis-spring-boot-autoconfigure 比较惹人注目,所以我们现在就看看这个项目。 首先我们看看 main/resources/META-INF/spring.factories,因为 Spring 很喜欢通过这些 meta 文件来促使各个模块很好的解耦但又能彼此配合工作,所以这个文件是定义 自动装配 开始的工厂类:
1 2 3 4
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\ org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
通过静态方法,传递一个 Class 类作为主资源,然后再传递给主资源数组的 run 方法。那么这个数组其实我们是可以传递多个主资源的,比如我们做项目的时候,想要每个模块包彼此分离,即可传递多个包的主资源路径。 new SpringApplication(primarySources).run(args); 才是真正的进入容器的准备阶段: 首先看看构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 14
publicSpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources){ this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); // 初始化主资源链表,用于下面需要读取的时候可以遍历. this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 判断Web环境,有webFlux/web/普通三个环境,主要通过类路径是否带有相对应需要的类来判断,如果都没有则初始化为普通Java项目. this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 初始化环境,这个接口多用于web环境,因为需要从web上下文加载一些信息. setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 初始化监听器,监听容器生命周期中需要回调的函数 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 从运行堆栈中寻找运行这个类的main方法所在的类 this.mainApplicationClass = deduceMainApplicationClass(); }
// Destroy already created singletons to avoid dangling resources. destroyBeans();
// Reset 'active' flag. cancelRefresh(ex);
// Propagate exception to caller. throw ex; }
finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor) if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } }
@Override publicvoidpostProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry){ int registryId = System.identityHashCode(registry); if (this.registriesPostProcessed.contains(registryId)) { thrownew IllegalStateException( "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); } if (this.factoriesPostProcessed.contains(registryId)) { thrownew IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + registry); } this.registriesPostProcessed.add(registryId);
/** * Build and validate a configuration model based on the registry of * {@link Configuration} classes. */ publicvoidprocessConfigBeanDefinitions(BeanDefinitionRegistry registry){ List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) { if (logger.isDebugEnabled()) { logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); } } elseif (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } }
// Return immediately if no @Configuration classes were found if (configCandidates.isEmpty()) { return; }
// Sort by previously determined @Order value, if applicable configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2); });
// Detect any custom bean name generation strategy supplied through the enclosing application context SingletonBeanRegistry sbr = null; if (registry instanceof SingletonBeanRegistry) { sbr = (SingletonBeanRegistry) registry; if (!this.localBeanNameGeneratorSet) { BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton( AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR); if (generator != null) { this.componentScanBeanNameGenerator = generator; this.importBeanNameGenerator = generator; } } }
if (this.environment == null) { this.environment = new StandardEnvironment(); }
// Parse each @Configuration class ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); // ↑ 这上面属于解析开始的准备工作 ↑ do { parser.parse(candidates); parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses);
candidates.clear(); if (registry.getBeanDefinitionCount() > candidateNames.length) { String[] newCandidateNames = registry.getBeanDefinitionNames(); Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); Set<String> alreadyParsedClasses = new HashSet<>(); for (ConfigurationClass configurationClass : alreadyParsed) { alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); } for (String candidateName : newCandidateNames) { if (!oldCandidateNames.contains(candidateName)) { BeanDefinition bd = registry.getBeanDefinition(candidateName); if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) { candidates.add(new BeanDefinitionHolder(bd, candidateName)); } } } candidateNames = newCandidateNames; } } while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); }
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) { // Clear cache in externally provided MetadataReaderFactory; this is a no-op // for a shared cache since it'll be cleared by the ApplicationContext. ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); } }
// 判断是否已经解析过了 ConfigurationClass existingClass = this.configurationClasses.get(configClass); if (existingClass != null) { if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } // Otherwise ignore new imported config class; existing non-imported class overrides it. return; } else { // Explicit bean definition found, probably replacing an import. // Let's remove the old one and go with the new one. this.configurationClasses.remove(configClass); this.knownSuperclasses.values().removeIf(configClass::equals); } }
// 递归解析配置类以及他的父类、接口等等 SourceClass sourceClass = asSourceClass(configClass); do { sourceClass = doProcessConfigurationClass(configClass, sourceClass); } while (sourceClass != null);
if (configClass.getMetadata().isAnnotated(Component.class.getName())) { // Recursively process any member (nested) classes first processMemberClasses(configClass, sourceClass); }
// 解析需要导入Property配置文件的类 for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); } else { logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } }
// 解析 @ComponentScan Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // The config class is annotated with @ComponentScan -> perform the scan immediately Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if needed for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } }
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass){ ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass){ ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
@SuppressWarnings("deprecation")// for Environment.acceptsProfiles(String...) protectedvoiddoRegisterBeanDefinitions(Element root){ // Any nested <beans> elements will cause recursion in this method. In // order to propagate and preserve <beans> default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. Create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); // We cannot use Profiles.of(...) since profile expressions are not supported // in XML config. See SPR-12458 for details. if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isDebugEnabled()) { logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } }
// 在这个方法中实现解析: protectedvoidparseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate){ if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { // 因为 <aop:aspectj-autoproxy/> 并不是默认的命名空间(默认只有:http://www.springframework.org/schema/beans), // 所以需要调用自定义的处理器来解析元素. delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
/** * Parse a custom element (outside of the default namespace). * @param ele the element to parse * @return the resulting bean definition */ @Nullable public BeanDefinition parseCustomElement(Element ele){ return parseCustomElement(ele, null); }
/** * Parse a custom element (outside of the default namespace). * @param ele the element to parse * @param containingBd the containing bean definition (if any) * @return the resulting bean definition */ @Nullable public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd){ // AOP的命名空间是http://www.springframework.org/schema/aop String namespaceUri = getNamespaceURI(ele); if (namespaceUri == null) { returnnull; } // 开始从上下文中寻找合适的解析器,第一个方法只是简单地返回一个实例 // 那下面直接看看 resolve 怎么处理的 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); returnnull; } return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }
@SuppressWarnings("deprecation")// for Environment.acceptsProfiles(String...) protectedvoiddoRegisterBeanDefinitions(Element root){ // Any nested <beans> elements will cause recursion in this method. In // order to propagate and preserve <beans> default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. Create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); // We cannot use Profiles.of(...) since profile expressions are not supported // in XML config. See SPR-12458 for details. if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isDebugEnabled()) { logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } }
// 在这个方法中实现解析: protectedvoidparseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate){ if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { // 因为 <aop:aspectj-autoproxy/> 并不是默认的命名空间(默认只有:http://www.springframework.org/schema/beans), // 所以需要调用自定义的处理器来解析元素. delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
/** * Parse a custom element (outside of the default namespace). * @param ele the element to parse * @return the resulting bean definition */ @Nullable public BeanDefinition parseCustomElement(Element ele){ return parseCustomElement(ele, null); }
/** * Parse a custom element (outside of the default namespace). * @param ele the element to parse * @param containingBd the containing bean definition (if any) * @return the resulting bean definition */ @Nullable public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd){ // AOP的命名空间是http://www.springframework.org/schema/aop String namespaceUri = getNamespaceURI(ele); if (namespaceUri == null) { returnnull; } // 开始从上下文中寻找合适的解析器,第一个方法只是简单地返回一个实例 // 那下面直接看看 resolve 怎么处理的 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); returnnull; } return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }
// Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }
if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } elseif (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { thrownew BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); } } } }
// 这里在前置的时候就开始记录哪些 Bean 需要织入,哪些不需要了 if (!StringUtils.hasLength(beanName) !this.targetSourcedBeans.contains(beanName)) { if (this.advisedBeans.containsKey(cacheKey)) { returnnull; } // 初始化第一个 Bean(在我这里是我那个配置类的初始化)的时候就开始调用 shouldSkip if (isInfrastructureClass(beanClass) shouldSkip(beanClass, beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); returnnull; } }
// Create proxy here if we have a custom TargetSource. // Suppresses unnecessary default instantiation of the target bean: // The TargetSource will handle target instances in a custom fashion. TargetSource targetSource = getCustomTargetSource(beanClass, beanName); if (targetSource != null) { if (StringUtils.hasLength(beanName)) { this.targetSourcedBeans.add(beanName); } Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource); Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; }
/** * Find all eligible Advisor beans in the current bean factory, * ignoring FactoryBeans and excluding beans that are currently in creation. * @return the list of {@link org.springframework.aop.Advisor} beans * @see #isEligibleBean */ public List<Advisor> findAdvisorBeans(){ // Determine list of advisor bean names, if not cached already. String[] advisorNames = this.cachedAdvisorBeanNames; if (advisorNames == null) { // Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let the auto-proxy creator apply to them! advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Advisor.class, true, false); this.cachedAdvisorBeanNames = advisorNames; } // 第一次调用的时候,还没找到,所以直接就在这里被返回出去 if (advisorNames.length == 0) { returnnew ArrayList<>(); }
List<Advisor> advisors = new ArrayList<>(); for (String name : advisorNames) { if (isEligibleBean(name)) { if (this.beanFactory.isCurrentlyInCreation(name)) { if (logger.isTraceEnabled()) { logger.trace("Skipping currently created advisor '" + name + "'"); } } else { try { advisors.add(this.beanFactory.getBean(name, Advisor.class)); } catch (BeanCreationException ex) { ..... continue; } } throw ex; } } } } return advisors; }
/** * Adds an {@link ExposeInvocationInterceptor} to the beginning of the advice chain. * These additional advices are needed when using AspectJ expression pointcuts * and when using AspectJ-style advice. */ @Override protectedvoidextendAdvisors(List<Advisor> candidateAdvisors){ AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors); }
publicstaticbooleanmakeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors){ // Don't add advisors to an empty list; may indicate that proxying is just not required if (!advisors.isEmpty()) { boolean foundAspectJAdvice = false; for (Advisor advisor : advisors) { // 如果是 AspectJAdvice 则不添加 if (isAspectJAdvice(advisor)) { foundAspectJAdvice = true; break; } } // 我们织入的方法中,都会添加一个 ExposeInvocationInterceptor 实例 if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) { advisors.add(0, ExposeInvocationInterceptor.ADVISOR); returntrue; } } returnfalse; }
@Override public Object getProxy(@Nullable ClassLoader classLoader){ if (logger.isTraceEnabled()) { logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource()); }
try { // 获取对象的实际类型 Class<?> rootClass = this.advised.getTargetClass(); Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
Callback[] callbacks = getCallbacks(rootClass); Class<?>[] types = new Class<?>[callbacks.length]; for (int x = 0; x < types.length; x++) { types[x] = callbacks[x].getClass(); } // fixedInterceptorMap only populated at this point, after getCallbacks call above enhancer.setCallbackFilter(new ProxyCallbackFilter( this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset)); enhancer.setCallbackTypes(types);
// 初始化代理对象 return createProxyClassAndInstance(enhancer, callbacks); } catch (CodeGenerationException IllegalArgumentException ex) { thrownew AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() + ": Common causes of this problem include using a final class or a non-visible class", ex); } catch (Throwable ex) { // TargetSource.getTarget() failed thrownew AopConfigException("Unexpected AOP exception", ex); } }
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { // Evaluate dynamic method matcher here: static part will already have // been evaluated and found to match. InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass()); if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) { return dm.interceptor.invoke(this); } else { // Dynamic matching failed. // Skip this interceptor and invoke the next in the chain. return proceed(); } } else { // It's an interceptor, so we just invoke it: The pointcut will have // been evaluated statically before this object was constructed. return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }
此时,第一个切入类是 Spring 的 ExposeInvocationInterceptor,直接走到下面的 return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); :
AspectJAroundAdvice#invoke: public Object invoke(MethodInvocation mi)throws Throwable { if (!(mi instanceof ProxyMethodInvocation)) { thrownew IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi); } ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi; // 这个参数就是我们写在 around 方法参数中的那个 // 此时我们的方法已经被织入在这个类的方法里边去 // 所以只要执行我们的 around 方法就是变相执行我们真实对象的方法了。 ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi); JoinPointMatch jpm = getJoinPointMatch(pmi); return invokeAdviceMethod(pjp, jpm, null, null); } // As above, but in this case we are given the join point. protected Object invokeAdviceMethod(JoinPoint jp, @Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable t)throws Throwable {
try { if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { // The target does not implement the equals(Object) method itself. return equals(args[0]); } elseif (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { // The target does not implement the hashCode() method itself. return hashCode(); } elseif (method.getDeclaringClass() == DecoratingProxy.class) { // There is only getDecoratedClass() declared -> dispatch to proxy config. return AopProxyUtils.ultimateTargetClass(this.advised); } elseif (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) { // Service invocations on ProxyConfig with the proxy config... return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args); }
Object retVal;
// 上面均是特殊方法的代理形式,直接走到这里 if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; }
// Get as late as possible to minimize the time we "own" the target, // in case it comes from a pool. target = targetSource.getTarget(); Class<?> targetClass = (target != null ? target.getClass() : null);
// Check whether we have any advice. If we don't, we can fallback on direct // reflective invocation of the target, and avoid creating a MethodInvocation. if (chain.isEmpty()) { // We can skip creating a MethodInvocation: just invoke the target directly // Note that the final invoker must be an InvokerInterceptor so we know it does // nothing but a reflective operation on the target, and no hot swapping or fancy proxying. Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } else { // 这一步就跟上面一样了 MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // Proceed to the joinpoint through the interceptor chain. retVal = invocation.proceed(); }
// Massage return value if necessary. Class<?> returnType = method.getReturnType(); if (retVal != null && retVal == target && returnType != Object.class && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { // Special case: it returned "this" and the return type of the method // is type-compatible. Note that we can't help if the target sets // a reference to itself in another returned object. retVal = proxy; } elseif (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) { thrownew AopInvocationException( "Null return value from advice does not match primitive return type for: " + method); } return retVal; } finally { if (target != null && !targetSource.isStatic()) { // Must have come from TargetSource. targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } }
// Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }
if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } elseif (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { thrownew BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); } } } }
@SuppressWarnings("deprecation")// for Environment.acceptsProfiles(String...) protectedvoiddoRegisterBeanDefinitions(Element root){ // Any nested <beans> elements will cause recursion in this method. In // order to propagate and preserve <beans> default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. Create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); // We cannot use Profiles.of(...) since profile expressions are not supported // in XML config. See SPR-12458 for details. if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isDebugEnabled()) { logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } }
// 在这个方法中实现解析: protectedvoidparseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate){ if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { // 因为 <aop:aspectj-autoproxy/> 并不是默认的命名空间(默认只有:http://www.springframework.org/schema/beans), // 所以需要调用自定义的处理器来解析元素. delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
/** * Parse a custom element (outside of the default namespace). * @param ele the element to parse * @return the resulting bean definition */ @Nullable public BeanDefinition parseCustomElement(Element ele){ return parseCustomElement(ele, null); }
/** * Parse a custom element (outside of the default namespace). * @param ele the element to parse * @param containingBd the containing bean definition (if any) * @return the resulting bean definition */ @Nullable public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd){ // AOP的命名空间是http://www.springframework.org/schema/aop String namespaceUri = getNamespaceURI(ele); if (namespaceUri == null) { returnnull; } // 开始从上下文中寻找合适的解析器,第一个方法只是简单地返回一个实例 // 那下面直接看看 resolve 怎么处理的 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); returnnull; } return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }
// Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }
if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } elseif (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { thrownew BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); } } } }
// 这里在前置的时候就开始记录哪些 Bean 需要织入,哪些不需要了 if (!StringUtils.hasLength(beanName) !this.targetSourcedBeans.contains(beanName)) { if (this.advisedBeans.containsKey(cacheKey)) { returnnull; } // 初始化第一个 Bean(在我这里是我那个配置类的初始化)的时候就开始调用 shouldSkip if (isInfrastructureClass(beanClass) shouldSkip(beanClass, beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); returnnull; } }
// Create proxy here if we have a custom TargetSource. // Suppresses unnecessary default instantiation of the target bean: // The TargetSource will handle target instances in a custom fashion. TargetSource targetSource = getCustomTargetSource(beanClass, beanName); if (targetSource != null) { if (StringUtils.hasLength(beanName)) { this.targetSourcedBeans.add(beanName); } Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource); Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; }
/** * Find all eligible Advisor beans in the current bean factory, * ignoring FactoryBeans and excluding beans that are currently in creation. * @return the list of {@link org.springframework.aop.Advisor} beans * @see #isEligibleBean */ public List<Advisor> findAdvisorBeans(){ // Determine list of advisor bean names, if not cached already. String[] advisorNames = this.cachedAdvisorBeanNames; if (advisorNames == null) { // Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let the auto-proxy creator apply to them! advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Advisor.class, true, false); this.cachedAdvisorBeanNames = advisorNames; } // 第一次调用的时候,还没找到,所以直接就在这里被返回出去 if (advisorNames.length == 0) { returnnew ArrayList<>(); }
List<Advisor> advisors = new ArrayList<>(); for (String name : advisorNames) { if (isEligibleBean(name)) { if (this.beanFactory.isCurrentlyInCreation(name)) { if (logger.isTraceEnabled()) { logger.trace("Skipping currently created advisor '" + name + "'"); } } else { try { advisors.add(this.beanFactory.getBean(name, Advisor.class)); } catch (BeanCreationException ex) { ..... continue; } } throw ex; } } } } return advisors; }
/** * Adds an {@link ExposeInvocationInterceptor} to the beginning of the advice chain. * These additional advices are needed when using AspectJ expression pointcuts * and when using AspectJ-style advice. */ @Override protectedvoidextendAdvisors(List<Advisor> candidateAdvisors){ AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors); }
publicstaticbooleanmakeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors){ // Don't add advisors to an empty list; may indicate that proxying is just not required if (!advisors.isEmpty()) { boolean foundAspectJAdvice = false; for (Advisor advisor : advisors) { // 如果是 AspectJAdvice 则不添加 if (isAspectJAdvice(advisor)) { foundAspectJAdvice = true; break; } } // 我们织入的方法中,都会添加一个 ExposeInvocationInterceptor 实例 if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) { advisors.add(0, ExposeInvocationInterceptor.ADVISOR); returntrue; } } returnfalse; }
@Override public Object getProxy(@Nullable ClassLoader classLoader){ if (logger.isTraceEnabled()) { logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource()); }
try { // 获取对象的实际类型 Class<?> rootClass = this.advised.getTargetClass(); Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
Callback[] callbacks = getCallbacks(rootClass); Class<?>[] types = new Class<?>[callbacks.length]; for (int x = 0; x < types.length; x++) { types[x] = callbacks[x].getClass(); } // fixedInterceptorMap only populated at this point, after getCallbacks call above enhancer.setCallbackFilter(new ProxyCallbackFilter( this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset)); enhancer.setCallbackTypes(types);
// 初始化代理对象 return createProxyClassAndInstance(enhancer, callbacks); } catch (CodeGenerationException IllegalArgumentException ex) { thrownew AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() + ": Common causes of this problem include using a final class or a non-visible class", ex); } catch (Throwable ex) { // TargetSource.getTarget() failed thrownew AopConfigException("Unexpected AOP exception", ex); } }
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { // Evaluate dynamic method matcher here: static part will already have // been evaluated and found to match. InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass()); if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) { return dm.interceptor.invoke(this); } else { // Dynamic matching failed. // Skip this interceptor and invoke the next in the chain. return proceed(); } } else { // It's an interceptor, so we just invoke it: The pointcut will have // been evaluated statically before this object was constructed. return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }
此时,第一个切入类是 Spring 的 ExposeInvocationInterceptor,直接走到下面的 return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); :
AspectJAroundAdvice#invoke: public Object invoke(MethodInvocation mi)throws Throwable { if (!(mi instanceof ProxyMethodInvocation)) { thrownew IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi); } ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi; // 这个参数就是我们写在 around 方法参数中的那个 // 此时我们的方法已经被织入在这个类的方法里边去 // 所以只要执行我们的 around 方法就是变相执行我们真实对象的方法了。 ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi); JoinPointMatch jpm = getJoinPointMatch(pmi); return invokeAdviceMethod(pjp, jpm, null, null); } // As above, but in this case we are given the join point. protected Object invokeAdviceMethod(JoinPoint jp, @Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable t)throws Throwable {
try { if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { // The target does not implement the equals(Object) method itself. return equals(args[0]); } elseif (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { // The target does not implement the hashCode() method itself. return hashCode(); } elseif (method.getDeclaringClass() == DecoratingProxy.class) { // There is only getDecoratedClass() declared -> dispatch to proxy config. return AopProxyUtils.ultimateTargetClass(this.advised); } elseif (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) { // Service invocations on ProxyConfig with the proxy config... return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args); }
Object retVal;
// 上面均是特殊方法的代理形式,直接走到这里 if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; }
// Get as late as possible to minimize the time we "own" the target, // in case it comes from a pool. target = targetSource.getTarget(); Class<?> targetClass = (target != null ? target.getClass() : null);
// Check whether we have any advice. If we don't, we can fallback on direct // reflective invocation of the target, and avoid creating a MethodInvocation. if (chain.isEmpty()) { // We can skip creating a MethodInvocation: just invoke the target directly // Note that the final invoker must be an InvokerInterceptor so we know it does // nothing but a reflective operation on the target, and no hot swapping or fancy proxying. Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } else { // 这一步就跟上面一样了 MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // Proceed to the joinpoint through the interceptor chain. retVal = invocation.proceed(); }
// Massage return value if necessary. Class<?> returnType = method.getReturnType(); if (retVal != null && retVal == target && returnType != Object.class && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { // Special case: it returned "this" and the return type of the method // is type-compatible. Note that we can't help if the target sets // a reference to itself in another returned object. retVal = proxy; } elseif (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) { thrownew AopInvocationException( "Null return value from advice does not match primitive return type for: " + method); } return retVal; } finally { if (target != null && !targetSource.isStatic()) { // Must have come from TargetSource. targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } }
// 这里在前置的时候就开始记录哪些 Bean 需要织入,哪些不需要了 if (!StringUtils.hasLength(beanName) !this.targetSourcedBeans.contains(beanName)) { if (this.advisedBeans.containsKey(cacheKey)) { returnnull; } // 初始化第一个 Bean(在我这里是我那个配置类的初始化)的时候就开始调用 shouldSkip if (isInfrastructureClass(beanClass) shouldSkip(beanClass, beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); returnnull; } }
// Create proxy here if we have a custom TargetSource. // Suppresses unnecessary default instantiation of the target bean: // The TargetSource will handle target instances in a custom fashion. TargetSource targetSource = getCustomTargetSource(beanClass, beanName); if (targetSource != null) { if (StringUtils.hasLength(beanName)) { this.targetSourcedBeans.add(beanName); } Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource); Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; }
/** * Find all eligible Advisor beans in the current bean factory, * ignoring FactoryBeans and excluding beans that are currently in creation. * @return the list of {@link org.springframework.aop.Advisor} beans * @see #isEligibleBean */ public List<Advisor> findAdvisorBeans(){ // Determine list of advisor bean names, if not cached already. String[] advisorNames = this.cachedAdvisorBeanNames; if (advisorNames == null) { // Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let the auto-proxy creator apply to them! advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Advisor.class, true, false); this.cachedAdvisorBeanNames = advisorNames; } // 第一次调用的时候,还没找到,所以直接就在这里被返回出去 if (advisorNames.length == 0) { returnnew ArrayList<>(); }
List<Advisor> advisors = new ArrayList<>(); for (String name : advisorNames) { if (isEligibleBean(name)) { if (this.beanFactory.isCurrentlyInCreation(name)) { if (logger.isTraceEnabled()) { logger.trace("Skipping currently created advisor '" + name + "'"); } } else { try { advisors.add(this.beanFactory.getBean(name, Advisor.class)); } catch (BeanCreationException ex) { ..... continue; } } throw ex; } } } } return advisors; }
/** * Adds an {@link ExposeInvocationInterceptor} to the beginning of the advice chain. * These additional advices are needed when using AspectJ expression pointcuts * and when using AspectJ-style advice. */ @Override protectedvoidextendAdvisors(List<Advisor> candidateAdvisors){ AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors); }
publicstaticbooleanmakeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors){ // Don't add advisors to an empty list; may indicate that proxying is just not required if (!advisors.isEmpty()) { boolean foundAspectJAdvice = false; for (Advisor advisor : advisors) { // 如果是 AspectJAdvice 则不添加 if (isAspectJAdvice(advisor)) { foundAspectJAdvice = true; break; } } // 我们织入的方法中,都会添加一个 ExposeInvocationInterceptor 实例 if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) { advisors.add(0, ExposeInvocationInterceptor.ADVISOR); returntrue; } } returnfalse; }
@Override public Object getProxy(@Nullable ClassLoader classLoader){ if (logger.isTraceEnabled()) { logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource()); }
try { // 获取对象的实际类型 Class<?> rootClass = this.advised.getTargetClass(); Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
Callback[] callbacks = getCallbacks(rootClass); Class<?>[] types = new Class<?>[callbacks.length]; for (int x = 0; x < types.length; x++) { types[x] = callbacks[x].getClass(); } // fixedInterceptorMap only populated at this point, after getCallbacks call above enhancer.setCallbackFilter(new ProxyCallbackFilter( this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset)); enhancer.setCallbackTypes(types);
// 初始化代理对象 return createProxyClassAndInstance(enhancer, callbacks); } catch (CodeGenerationException IllegalArgumentException ex) { thrownew AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() + ": Common causes of this problem include using a final class or a non-visible class", ex); } catch (Throwable ex) { // TargetSource.getTarget() failed thrownew AopConfigException("Unexpected AOP exception", ex); } }
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { // Evaluate dynamic method matcher here: static part will already have // been evaluated and found to match. InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass()); if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) { return dm.interceptor.invoke(this); } else { // Dynamic matching failed. // Skip this interceptor and invoke the next in the chain. return proceed(); } } else { // It's an interceptor, so we just invoke it: The pointcut will have // been evaluated statically before this object was constructed. return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }
此时,第一个切入类是 Spring 的 ExposeInvocationInterceptor,直接走到下面的 return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); :
AspectJAroundAdvice#invoke: public Object invoke(MethodInvocation mi)throws Throwable { if (!(mi instanceof ProxyMethodInvocation)) { thrownew IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi); } ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi; // 这个参数就是我们写在 around 方法参数中的那个 // 此时我们的方法已经被织入在这个类的方法里边去 // 所以只要执行我们的 around 方法就是变相执行我们真实对象的方法了。 ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi); JoinPointMatch jpm = getJoinPointMatch(pmi); return invokeAdviceMethod(pjp, jpm, null, null); } // As above, but in this case we are given the join point. protected Object invokeAdviceMethod(JoinPoint jp, @Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable t)throws Throwable {
try { if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { // The target does not implement the equals(Object) method itself. return equals(args[0]); } elseif (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { // The target does not implement the hashCode() method itself. return hashCode(); } elseif (method.getDeclaringClass() == DecoratingProxy.class) { // There is only getDecoratedClass() declared -> dispatch to proxy config. return AopProxyUtils.ultimateTargetClass(this.advised); } elseif (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) { // Service invocations on ProxyConfig with the proxy config... return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args); }
Object retVal;
// 上面均是特殊方法的代理形式,直接走到这里 if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; }
// Get as late as possible to minimize the time we "own" the target, // in case it comes from a pool. target = targetSource.getTarget(); Class<?> targetClass = (target != null ? target.getClass() : null);
// Check whether we have any advice. If we don't, we can fallback on direct // reflective invocation of the target, and avoid creating a MethodInvocation. if (chain.isEmpty()) { // We can skip creating a MethodInvocation: just invoke the target directly // Note that the final invoker must be an InvokerInterceptor so we know it does // nothing but a reflective operation on the target, and no hot swapping or fancy proxying. Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } else { // 这一步就跟上面一样了 MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // Proceed to the joinpoint through the interceptor chain. retVal = invocation.proceed(); }
// Massage return value if necessary. Class<?> returnType = method.getReturnType(); if (retVal != null && retVal == target && returnType != Object.class && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { // Special case: it returned "this" and the return type of the method // is type-compatible. Note that we can't help if the target sets // a reference to itself in another returned object. retVal = proxy; } elseif (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) { thrownew AopInvocationException( "Null return value from advice does not match primitive return type for: " + method); } return retVal; } finally { if (target != null && !targetSource.isStatic()) { // Must have come from TargetSource. targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } }
// Destroy already created singletons to avoid dangling resources. destroyBeans();
// Reset 'active' flag. cancelRefresh(ex);
// Propagate exception to caller. throw ex; }
finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
// 下面代码都是初始化 applicationListeners 的容器 // 也就是 applicationContext 启动前我们注册的事件监听器 if (this.earlyApplicationListeners == null) { this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners); } else { // Reset local application listeners to pre-refresh state. this.applicationListeners.clear(); this.applicationListeners.addAll(this.earlyApplicationListeners); }
// Allow for the collection of early ApplicationEvents, // to be published once the multicaster is available... this.earlyApplicationEvents = new LinkedHashSet<>(); }
初始化我感觉没做什么特别的事情,也就是初始化一些必要的容器。 接下来就是大事情了:
1 2
// Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
@Override protectedvoidloadBeanDefinitions(DefaultListableBeanFactory beanFactory)throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); }
protectedvoidprepareBeanFactory(ConfigurableListableBeanFactory beanFactory){ // Tell the internal bean factory to use the context's class loader etc. beanFactory.setBeanClassLoader(getClassLoader()); beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())); beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// 指定类在加载时织入AOP操作 if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); // Set a temporary ClassLoader for type matching. beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); }
// 一些记录环境信息的Bean if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment()); } if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties()); } if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment()); } }
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor) if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } }
// Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let the bean factory post-processors apply to them! // Separate between BeanDefinitionRegistryPostProcessors that implement // PriorityOrdered, Ordered, and the rest. // 在这里不初始化 FactoryBeans 从而可以让 beanfactory 的 post-processors 来处理他们 // 因为下面需要排序这些配置 Bean 的执行顺序。 List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
/* 下面这些目前可以先不用看了。是用来执行其他 BeanDefinitionRegistryPostProcessors 因为我目前的主要点在于怎么解析我的配置类。 */ // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear. boolean reiterate = true; while (reiterate) { reiterate = false; postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { if (!processedBeans.contains(ppName)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); reiterate = true; } } sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors); invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); currentRegistryProcessors.clear(); }
// Now, invoke the postProcessBeanFactory callback of all processors handled so far. invokeBeanFactoryPostProcessors(registryProcessors, beanFactory); invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory); }
else { // Invoke factory processors registered with the context instance. invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory); }
// Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let the bean factory post-processors apply to them! String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
// Separate between BeanFactoryPostProcessors that implement PriorityOrdered, // Ordered, and the rest. List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>(); List<String> orderedPostProcessorNames = new ArrayList<>(); List<String> nonOrderedPostProcessorNames = new ArrayList<>(); for (String ppName : postProcessorNames) { if (processedBeans.contains(ppName)) { // skip - already processed in first phase above } elseif (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class)); } elseif (beanFactory.isTypeMatch(ppName, Ordered.class)) { orderedPostProcessorNames.add(ppName); } else { nonOrderedPostProcessorNames.add(ppName); } }
// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered. sortPostProcessors(priorityOrderedPostProcessors, beanFactory); invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
// Next, invoke the BeanFactoryPostProcessors that implement Ordered. List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size()); for (String postProcessorName : orderedPostProcessorNames) { orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); } sortPostProcessors(orderedPostProcessors, beanFactory); invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
// Finally, invoke all other BeanFactoryPostProcessors. List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size()); for (String postProcessorName : nonOrderedPostProcessorNames) { nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); } invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
// Clear cached merged bean definitions since the post-processors might have // modified the original metadata, e.g. replacing placeholders in values... beanFactory.clearMetadataCache(); }
好我现在要在装配那部分开始进入了,因为 Spring 在开始的时候调用了 ConfigurationClassPostProcessor 的 postProcessBeanDefinitionRegistry 方法,现在要看看这个方法做了什么事情。
@Override publicvoidpostProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry){ int registryId = System.identityHashCode(registry); if (this.registriesPostProcessed.contains(registryId)) { thrownew IllegalStateException( "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); } if (this.factoriesPostProcessed.contains(registryId)) { thrownew IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + registry); } this.registriesPostProcessed.add(registryId);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); do { parser.parse(candidates); parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed);
// 使用 ConfigurationClassBeanDefinitionReader 来解析配置类 // 这一步就跟 xml reader 一样,解析里面各个标签 if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses);
candidates.clear(); if (registry.getBeanDefinitionCount() > candidateNames.length) { String[] newCandidateNames = registry.getBeanDefinitionNames(); Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); Set<String> alreadyParsedClasses = new HashSet<>(); for (ConfigurationClass configurationClass : alreadyParsed) { alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); } for (String candidateName : newCandidateNames) { if (!oldCandidateNames.contains(candidateName)) { BeanDefinition bd = registry.getBeanDefinition(candidateName); if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) { candidates.add(new BeanDefinitionHolder(bd, candidateName)); } } } candidateNames = newCandidateNames; } } while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); }
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) { // Clear cache in externally provided MetadataReaderFactory; this is a no-op // for a shared cache since it'll be cleared by the ApplicationContext. ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); } }
protectedfinalvoidparse(@Nullable String className, String beanName)throws IOException { Assert.notNull(className, "No bean class name for configuration class bean definition"); MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className); processConfigurationClass(new ConfigurationClass(reader, beanName)); } protectedvoidprocessConfigurationClass(ConfigurationClass configClass)throws IOException { if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { return; }
ConfigurationClass existingClass = this.configurationClasses.get(configClass); if (existingClass != null) { if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } // Otherwise ignore new imported config class; existing non-imported class overrides it. return; } else { // Explicit bean definition found, probably replacing an import. // Let's remove the old one and go with the new one. this.configurationClasses.remove(configClass); this.knownSuperclasses.values().removeIf(configClass::equals); } }
if (configClass.getMetadata().isAnnotated(Component.class.getName())) { // 解析内部类 processMemberClasses(configClass, sourceClass); }
// 解析配置类(SpringBoot 熟悉的写配置自动提示那种配置) for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); } else { logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } }
// 解析 @ComponentScan 这块打算后面再看了 Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // The config class is annotated with @ComponentScan -> perform the scan immediately Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if needed for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } }
// Consider name and any aliases List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name"))); // 这一步让我知道了,方法名就是 bean 的名字= = String beanName = (!names.isEmpty() ? names.remove(0) : methodName);
// Register aliases even when overridden for (String alias : names) { this.registry.registerAlias(beanName, alias); }
// 判断 xml 配置的 override 是否跟 JavaConfig 冲突了 if (isOverriddenByExistingDefinition(beanMethod, beanName)) { if (beanName.equals(beanMethod.getConfigurationClass().getBeanName())) { thrownew BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(), beanName, "Bean name derived from @Bean method '" + beanMethod.getMetadata().getMethodName() + "' clashes with bean name for containing configuration class; please make those names unique!"); } return; }
ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata); beanDef.setResource(configClass.getResource()); beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
// Now, invoke the postProcessBeanFactory callback of all processors handled so far. invokeBeanFactoryPostProcessors(registryProcessors, beanFactory); invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory); }
else { // Invoke factory processors registered with the context instance. invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory); }
// Separate between BeanFactoryPostProcessors that implement PriorityOrdered, // Ordered, and the rest. List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>(); List<String> orderedPostProcessorNames = new ArrayList<>(); List<String> nonOrderedPostProcessorNames = new ArrayList<>(); for (String ppName : postProcessorNames) { if (processedBeans.contains(ppName)) { // skip - already processed in first phase above } elseif (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class)); } elseif (beanFactory.isTypeMatch(ppName, Ordered.class)) { orderedPostProcessorNames.add(ppName); } else { nonOrderedPostProcessorNames.add(ppName); } }
// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered. sortPostProcessors(priorityOrderedPostProcessors, beanFactory); invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
// Next, invoke the BeanFactoryPostProcessors that implement Ordered. List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size()); for (String postProcessorName : orderedPostProcessorNames) { orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); } sortPostProcessors(orderedPostProcessors, beanFactory); invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
// Finally, invoke all other BeanFactoryPostProcessors. List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size()); for (String postProcessorName : nonOrderedPostProcessorNames) { nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); } invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
// Clear cached merged bean definitions since the post-processors might have // modified the original metadata, e.g. replacing placeholders in values... beanFactory.clearMetadataCache();
DefaultListableBeanFactory#preInstantiateSingletons @Override publicvoidpreInstantiateSingletons()throws BeansException { if (logger.isTraceEnabled()) { logger.trace("Pre-instantiating singletons in " + this); }
// Iterate over a copy to allow for init methods which in turn register new bean definitions. // While this may not be part of the regular factory bootstrap, it does otherwise work fine. List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Destroy already created singletons to avoid dangling resources. destroyBeans();
// Reset 'active' flag. cancelRefresh(ex);
// Propagate exception to caller. throw ex; }
finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
// 下面代码都是初始化 applicationListeners 的容器 // 也就是 applicationContext 启动前我们注册的事件监听器 if (this.earlyApplicationListeners == null) { this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners); } else { // Reset local application listeners to pre-refresh state. this.applicationListeners.clear(); this.applicationListeners.addAll(this.earlyApplicationListeners); }
// Allow for the collection of early ApplicationEvents, // to be published once the multicaster is available... this.earlyApplicationEvents = new LinkedHashSet<>(); }
初始化我感觉没做什么特别的事情,也就是初始化一些必要的容器。 接下来就是大事情了:
1 2
// Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Destroy already created singletons to avoid dangling resources. destroyBeans();
// Reset 'active' flag. cancelRefresh(ex);
// Propagate exception to caller. throw ex; }
finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
// 下面代码都是初始化 applicationListeners 的容器 // 也就是 applicationContext 启动前我们注册的事件监听器 if (this.earlyApplicationListeners == null) { this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners); } else { // Reset local application listeners to pre-refresh state. this.applicationListeners.clear(); this.applicationListeners.addAll(this.earlyApplicationListeners); }
// Allow for the collection of early ApplicationEvents, // to be published once the multicaster is available... this.earlyApplicationEvents = new LinkedHashSet<>(); }
初始化我感觉没做什么特别的事情,也就是初始化一些必要的容器。 接下来就是大事情了:
1 2
// Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
@Override protectedvoidloadBeanDefinitions(DefaultListableBeanFactory beanFactory)throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); }
protectedvoidprepareBeanFactory(ConfigurableListableBeanFactory beanFactory){ // Tell the internal bean factory to use the context's class loader etc. beanFactory.setBeanClassLoader(getClassLoader()); beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())); beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// 指定类在加载时织入AOP操作 if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); // Set a temporary ClassLoader for type matching. beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); }
// 一些记录环境信息的Bean if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment()); } if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties()); } if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment()); } }
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor) if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } }
// Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let the bean factory post-processors apply to them! // Separate between BeanDefinitionRegistryPostProcessors that implement // PriorityOrdered, Ordered, and the rest. // 在这里不初始化 FactoryBeans 从而可以让 beanfactory 的 post-processors 来处理他们 // 因为下面需要排序这些配置 Bean 的执行顺序。 List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
/* 下面这些目前可以先不用看了。是用来执行其他 BeanDefinitionRegistryPostProcessors 因为我目前的主要点在于怎么解析我的配置类。 */ // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear. boolean reiterate = true; while (reiterate) { reiterate = false; postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { if (!processedBeans.contains(ppName)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); reiterate = true; } } sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors); invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); currentRegistryProcessors.clear(); }
// Now, invoke the postProcessBeanFactory callback of all processors handled so far. invokeBeanFactoryPostProcessors(registryProcessors, beanFactory); invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory); }
else { // Invoke factory processors registered with the context instance. invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory); }
// Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let the bean factory post-processors apply to them! String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
// Separate between BeanFactoryPostProcessors that implement PriorityOrdered, // Ordered, and the rest. List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>(); List<String> orderedPostProcessorNames = new ArrayList<>(); List<String> nonOrderedPostProcessorNames = new ArrayList<>(); for (String ppName : postProcessorNames) { if (processedBeans.contains(ppName)) { // skip - already processed in first phase above } elseif (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class)); } elseif (beanFactory.isTypeMatch(ppName, Ordered.class)) { orderedPostProcessorNames.add(ppName); } else { nonOrderedPostProcessorNames.add(ppName); } }
// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered. sortPostProcessors(priorityOrderedPostProcessors, beanFactory); invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
// Next, invoke the BeanFactoryPostProcessors that implement Ordered. List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size()); for (String postProcessorName : orderedPostProcessorNames) { orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); } sortPostProcessors(orderedPostProcessors, beanFactory); invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
// Finally, invoke all other BeanFactoryPostProcessors. List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size()); for (String postProcessorName : nonOrderedPostProcessorNames) { nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); } invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
// Clear cached merged bean definitions since the post-processors might have // modified the original metadata, e.g. replacing placeholders in values... beanFactory.clearMetadataCache(); }
好我现在要在装配那部分开始进入了,因为 Spring 在开始的时候调用了 ConfigurationClassPostProcessor 的 postProcessBeanDefinitionRegistry 方法,现在要看看这个方法做了什么事情。
@Override publicvoidpostProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry){ int registryId = System.identityHashCode(registry); if (this.registriesPostProcessed.contains(registryId)) { thrownew IllegalStateException( "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); } if (this.factoriesPostProcessed.contains(registryId)) { thrownew IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + registry); } this.registriesPostProcessed.add(registryId);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); do { parser.parse(candidates); parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed);
// 使用 ConfigurationClassBeanDefinitionReader 来解析配置类 // 这一步就跟 xml reader 一样,解析里面各个标签 if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses);
candidates.clear(); if (registry.getBeanDefinitionCount() > candidateNames.length) { String[] newCandidateNames = registry.getBeanDefinitionNames(); Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); Set<String> alreadyParsedClasses = new HashSet<>(); for (ConfigurationClass configurationClass : alreadyParsed) { alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); } for (String candidateName : newCandidateNames) { if (!oldCandidateNames.contains(candidateName)) { BeanDefinition bd = registry.getBeanDefinition(candidateName); if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) { candidates.add(new BeanDefinitionHolder(bd, candidateName)); } } } candidateNames = newCandidateNames; } } while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); }
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) { // Clear cache in externally provided MetadataReaderFactory; this is a no-op // for a shared cache since it'll be cleared by the ApplicationContext. ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); } }
protectedfinalvoidparse(@Nullable String className, String beanName)throws IOException { Assert.notNull(className, "No bean class name for configuration class bean definition"); MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className); processConfigurationClass(new ConfigurationClass(reader, beanName)); } protectedvoidprocessConfigurationClass(ConfigurationClass configClass)throws IOException { if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { return; }
ConfigurationClass existingClass = this.configurationClasses.get(configClass); if (existingClass != null) { if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } // Otherwise ignore new imported config class; existing non-imported class overrides it. return; } else { // Explicit bean definition found, probably replacing an import. // Let's remove the old one and go with the new one. this.configurationClasses.remove(configClass); this.knownSuperclasses.values().removeIf(configClass::equals); } }
if (configClass.getMetadata().isAnnotated(Component.class.getName())) { // 解析内部类 processMemberClasses(configClass, sourceClass); }
// 解析配置类(SpringBoot 熟悉的写配置自动提示那种配置) for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); } else { logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } }
// 解析 @ComponentScan 这块打算后面再看了 Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // The config class is annotated with @ComponentScan -> perform the scan immediately Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if needed for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } }
// Consider name and any aliases List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name"))); // 这一步让我知道了,方法名就是 bean 的名字= = String beanName = (!names.isEmpty() ? names.remove(0) : methodName);
// Register aliases even when overridden for (String alias : names) { this.registry.registerAlias(beanName, alias); }
// 判断 xml 配置的 override 是否跟 JavaConfig 冲突了 if (isOverriddenByExistingDefinition(beanMethod, beanName)) { if (beanName.equals(beanMethod.getConfigurationClass().getBeanName())) { thrownew BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(), beanName, "Bean name derived from @Bean method '" + beanMethod.getMetadata().getMethodName() + "' clashes with bean name for containing configuration class; please make those names unique!"); } return; }
ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata); beanDef.setResource(configClass.getResource()); beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
// Now, invoke the postProcessBeanFactory callback of all processors handled so far. invokeBeanFactoryPostProcessors(registryProcessors, beanFactory); invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory); }
else { // Invoke factory processors registered with the context instance. invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory); }
// Separate between BeanFactoryPostProcessors that implement PriorityOrdered, // Ordered, and the rest. List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>(); List<String> orderedPostProcessorNames = new ArrayList<>(); List<String> nonOrderedPostProcessorNames = new ArrayList<>(); for (String ppName : postProcessorNames) { if (processedBeans.contains(ppName)) { // skip - already processed in first phase above } elseif (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class)); } elseif (beanFactory.isTypeMatch(ppName, Ordered.class)) { orderedPostProcessorNames.add(ppName); } else { nonOrderedPostProcessorNames.add(ppName); } }
// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered. sortPostProcessors(priorityOrderedPostProcessors, beanFactory); invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
// Next, invoke the BeanFactoryPostProcessors that implement Ordered. List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size()); for (String postProcessorName : orderedPostProcessorNames) { orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); } sortPostProcessors(orderedPostProcessors, beanFactory); invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
// Finally, invoke all other BeanFactoryPostProcessors. List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size()); for (String postProcessorName : nonOrderedPostProcessorNames) { nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); } invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
// Clear cached merged bean definitions since the post-processors might have // modified the original metadata, e.g. replacing placeholders in values... beanFactory.clearMetadataCache();
DefaultListableBeanFactory#preInstantiateSingletons @Override publicvoidpreInstantiateSingletons()throws BeansException { if (logger.isTraceEnabled()) { logger.trace("Pre-instantiating singletons in " + this); }
// Iterate over a copy to allow for init methods which in turn register new bean definitions. // While this may not be part of the regular factory bootstrap, it does otherwise work fine. List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
@Override protectedvoidloadBeanDefinitions(DefaultListableBeanFactory beanFactory)throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); }
protectedvoidprepareBeanFactory(ConfigurableListableBeanFactory beanFactory){ // Tell the internal bean factory to use the context's class loader etc. beanFactory.setBeanClassLoader(getClassLoader()); beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())); beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// 指定类在加载时织入AOP操作 if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); // Set a temporary ClassLoader for type matching. beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); }
// 一些记录环境信息的Bean if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment()); } if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties()); } if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment()); } }
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor) if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } }
// Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let the bean factory post-processors apply to them! // Separate between BeanDefinitionRegistryPostProcessors that implement // PriorityOrdered, Ordered, and the rest. // 在这里不初始化 FactoryBeans 从而可以让 beanfactory 的 post-processors 来处理他们 // 因为下面需要排序这些配置 Bean 的执行顺序。 List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
/* 下面这些目前可以先不用看了。是用来执行其他 BeanDefinitionRegistryPostProcessors 因为我目前的主要点在于怎么解析我的配置类。 */ // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear. boolean reiterate = true; while (reiterate) { reiterate = false; postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { if (!processedBeans.contains(ppName)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); reiterate = true; } } sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors); invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); currentRegistryProcessors.clear(); }
// Now, invoke the postProcessBeanFactory callback of all processors handled so far. invokeBeanFactoryPostProcessors(registryProcessors, beanFactory); invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory); }
else { // Invoke factory processors registered with the context instance. invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory); }
// Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let the bean factory post-processors apply to them! String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
// Separate between BeanFactoryPostProcessors that implement PriorityOrdered, // Ordered, and the rest. List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>(); List<String> orderedPostProcessorNames = new ArrayList<>(); List<String> nonOrderedPostProcessorNames = new ArrayList<>(); for (String ppName : postProcessorNames) { if (processedBeans.contains(ppName)) { // skip - already processed in first phase above } elseif (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class)); } elseif (beanFactory.isTypeMatch(ppName, Ordered.class)) { orderedPostProcessorNames.add(ppName); } else { nonOrderedPostProcessorNames.add(ppName); } }
// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered. sortPostProcessors(priorityOrderedPostProcessors, beanFactory); invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
// Next, invoke the BeanFactoryPostProcessors that implement Ordered. List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size()); for (String postProcessorName : orderedPostProcessorNames) { orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); } sortPostProcessors(orderedPostProcessors, beanFactory); invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
// Finally, invoke all other BeanFactoryPostProcessors. List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size()); for (String postProcessorName : nonOrderedPostProcessorNames) { nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); } invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
// Clear cached merged bean definitions since the post-processors might have // modified the original metadata, e.g. replacing placeholders in values... beanFactory.clearMetadataCache(); }
好我现在要在装配那部分开始进入了,因为 Spring 在开始的时候调用了 ConfigurationClassPostProcessor 的 postProcessBeanDefinitionRegistry 方法,现在要看看这个方法做了什么事情。
@Override publicvoidpostProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry){ int registryId = System.identityHashCode(registry); if (this.registriesPostProcessed.contains(registryId)) { thrownew IllegalStateException( "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); } if (this.factoriesPostProcessed.contains(registryId)) { thrownew IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + registry); } this.registriesPostProcessed.add(registryId);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); do { parser.parse(candidates); parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed);
// 使用 ConfigurationClassBeanDefinitionReader 来解析配置类 // 这一步就跟 xml reader 一样,解析里面各个标签 if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses);
candidates.clear(); if (registry.getBeanDefinitionCount() > candidateNames.length) { String[] newCandidateNames = registry.getBeanDefinitionNames(); Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); Set<String> alreadyParsedClasses = new HashSet<>(); for (ConfigurationClass configurationClass : alreadyParsed) { alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); } for (String candidateName : newCandidateNames) { if (!oldCandidateNames.contains(candidateName)) { BeanDefinition bd = registry.getBeanDefinition(candidateName); if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) { candidates.add(new BeanDefinitionHolder(bd, candidateName)); } } } candidateNames = newCandidateNames; } } while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); }
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) { // Clear cache in externally provided MetadataReaderFactory; this is a no-op // for a shared cache since it'll be cleared by the ApplicationContext. ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); } }
protectedfinalvoidparse(@Nullable String className, String beanName)throws IOException { Assert.notNull(className, "No bean class name for configuration class bean definition"); MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className); processConfigurationClass(new ConfigurationClass(reader, beanName)); } protectedvoidprocessConfigurationClass(ConfigurationClass configClass)throws IOException { if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { return; }
ConfigurationClass existingClass = this.configurationClasses.get(configClass); if (existingClass != null) { if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } // Otherwise ignore new imported config class; existing non-imported class overrides it. return; } else { // Explicit bean definition found, probably replacing an import. // Let's remove the old one and go with the new one. this.configurationClasses.remove(configClass); this.knownSuperclasses.values().removeIf(configClass::equals); } }
if (configClass.getMetadata().isAnnotated(Component.class.getName())) { // 解析内部类 processMemberClasses(configClass, sourceClass); }
// 解析配置类(SpringBoot 熟悉的写配置自动提示那种配置) for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); } else { logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } }
// 解析 @ComponentScan 这块打算后面再看了 Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // The config class is annotated with @ComponentScan -> perform the scan immediately Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if needed for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } }
// Consider name and any aliases List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name"))); // 这一步让我知道了,方法名就是 bean 的名字= = String beanName = (!names.isEmpty() ? names.remove(0) : methodName);
// Register aliases even when overridden for (String alias : names) { this.registry.registerAlias(beanName, alias); }
// 判断 xml 配置的 override 是否跟 JavaConfig 冲突了 if (isOverriddenByExistingDefinition(beanMethod, beanName)) { if (beanName.equals(beanMethod.getConfigurationClass().getBeanName())) { thrownew BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(), beanName, "Bean name derived from @Bean method '" + beanMethod.getMetadata().getMethodName() + "' clashes with bean name for containing configuration class; please make those names unique!"); } return; }
ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata); beanDef.setResource(configClass.getResource()); beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
// Now, invoke the postProcessBeanFactory callback of all processors handled so far. invokeBeanFactoryPostProcessors(registryProcessors, beanFactory); invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory); }
else { // Invoke factory processors registered with the context instance. invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory); }
// Separate between BeanFactoryPostProcessors that implement PriorityOrdered, // Ordered, and the rest. List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>(); List<String> orderedPostProcessorNames = new ArrayList<>(); List<String> nonOrderedPostProcessorNames = new ArrayList<>(); for (String ppName : postProcessorNames) { if (processedBeans.contains(ppName)) { // skip - already processed in first phase above } elseif (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class)); } elseif (beanFactory.isTypeMatch(ppName, Ordered.class)) { orderedPostProcessorNames.add(ppName); } else { nonOrderedPostProcessorNames.add(ppName); } }
// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered. sortPostProcessors(priorityOrderedPostProcessors, beanFactory); invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
// Next, invoke the BeanFactoryPostProcessors that implement Ordered. List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size()); for (String postProcessorName : orderedPostProcessorNames) { orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); } sortPostProcessors(orderedPostProcessors, beanFactory); invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
// Finally, invoke all other BeanFactoryPostProcessors. List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size()); for (String postProcessorName : nonOrderedPostProcessorNames) { nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); } invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
// Clear cached merged bean definitions since the post-processors might have // modified the original metadata, e.g. replacing placeholders in values... beanFactory.clearMetadataCache();
DefaultListableBeanFactory#preInstantiateSingletons @Override publicvoidpreInstantiateSingletons()throws BeansException { if (logger.isTraceEnabled()) { logger.trace("Pre-instantiating singletons in " + this); }
// Iterate over a copy to allow for init methods which in turn register new bean definitions. // While this may not be part of the regular factory bootstrap, it does otherwise work fine. List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);